From a5566f5d5a3ee47a760cf27a77875144689d8b96 Mon Sep 17 00:00:00 2001 From: alja Date: Thu, 12 Oct 2023 14:38:41 -0700 Subject: [PATCH 001/276] Implement only-if-cached cache control using XrdPfcFsctl --- src/XProtocol/XProtocol.cc | 3 ++- src/XProtocol/XProtocol.hh | 3 +++ src/XrdHttp/XrdHttpProtocol.cc | 5 +++++ src/XrdHttp/XrdHttpReq.cc | 3 +++ src/XrdPfc/XrdPfcFSctl.cc | 18 ++++++++++++++++++ src/XrdPss/XrdPss.cc | 32 ++++++++++++++++++++++++++++++++ 6 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/XProtocol/XProtocol.cc b/src/XProtocol/XProtocol.cc index 1e1772d3fb0..da9e7461349 100644 --- a/src/XProtocol/XProtocol.cc +++ b/src/XProtocol/XProtocol.cc @@ -102,7 +102,8 @@ const char *errNames[kXR_ERRFENCE-kXR_ArgInvalid] = "Request is not possible", // kXR_Impossible "Conflicting request", // kXR_Conflict "Too many errors", // kXR_TooManyErrs - "Request timed out" // kXR_ReqTimedOut + "Request timed out", // kXR_ReqTimedOut + "Timer expired" // kXR_TimerExipred }; const char *reqNames[kXR_REQFENCE-kXR_auth] = diff --git a/src/XProtocol/XProtocol.hh b/src/XProtocol/XProtocol.hh index 561161d9913..b984f191d75 100644 --- a/src/XProtocol/XProtocol.hh +++ b/src/XProtocol/XProtocol.hh @@ -1020,6 +1020,7 @@ enum XErrorCode { kXR_Conflict, // 3032 kXR_TooManyErrs, // 3033 kXR_ReqTimedOut, // 3034 + kXR_TimerExpired, // 3035 kXR_ERRFENCE, // Always last valid errcode + 1 kXR_noErrorYet = 10000 }; @@ -1396,6 +1397,7 @@ static int mapError(int rc) case ETIMEDOUT: return kXR_ReqTimedOut; case EBADF: return kXR_FileNotOpen; case ECANCELED: return kXR_Cancelled; + case ETIME: return kXR_TimerExpired; default: return kXR_FSError; } } @@ -1438,6 +1440,7 @@ static int toErrno( int xerr ) case kXR_Conflict: return ENOTTY; case kXR_TooManyErrs: return ETOOMANYREFS; case kXR_ReqTimedOut: return ETIMEDOUT; + case kXR_TimerExpired: return ETIME; // Used for 504 Gateway timeout in proxy default: return ENOMSG; } } diff --git a/src/XrdHttp/XrdHttpProtocol.cc b/src/XrdHttp/XrdHttpProtocol.cc index 0001b251fe3..ad0a282071a 100644 --- a/src/XrdHttp/XrdHttpProtocol.cc +++ b/src/XrdHttp/XrdHttpProtocol.cc @@ -1085,6 +1085,10 @@ int XrdHttpProtocol::Config(const char *ConfigFN, XrdOucEnv *myEnv) { return 1; } +// Some headers must always be converted to CGI key=value pairs +// + hdr2cgimap["Cache-Control"] = "cache-control"; + // Test if XrdEC is loaded if (getenv("XRDCL_EC")) usingEC = true; @@ -1561,6 +1565,7 @@ int XrdHttpProtocol::StartSimpleResp(int code, const char *desc, const char *hea else if (code == 405) ss << "Method Not Allowed"; else if (code == 416) ss << "Range Not Satisfiable"; else if (code == 500) ss << "Internal Server Error"; + else if (code == 504) ss << "Gateway Timeout"; else ss << "Unknown"; } ss << crlf; diff --git a/src/XrdHttp/XrdHttpReq.cc b/src/XrdHttp/XrdHttpReq.cc index 7d4beed9b75..9a54293b52f 100644 --- a/src/XrdHttp/XrdHttpReq.cc +++ b/src/XrdHttp/XrdHttpReq.cc @@ -898,6 +898,9 @@ void XrdHttpReq::mapXrdErrorToHttpStatus() { case kXR_InvalidRequest: httpStatusCode = 405; httpStatusText = "Method is not allowed"; break; + case kXR_TimerExpired: + httpStatusCode = 504; httpStatusText = "Gateway timeout"; + break; default: break; } diff --git a/src/XrdPfc/XrdPfcFSctl.cc b/src/XrdPfc/XrdPfcFSctl.cc index 1a2f1f29952..b86622bbb52 100644 --- a/src/XrdPfc/XrdPfcFSctl.cc +++ b/src/XrdPfc/XrdPfcFSctl.cc @@ -35,6 +35,7 @@ #include "XrdOfs/XrdOfsHandle.hh" #include "XrdOuc/XrdOucEnv.hh" #include "XrdOuc/XrdOucErrInfo.hh" +#include "XrdOuc/XrdOucCache.hh" #include "XrdPfc/XrdPfc.hh" #include "XrdPfc/XrdPfcFSctl.hh" #include "XrdPfc/XrdPfcTrace.hh" @@ -131,6 +132,23 @@ int XrdPfcFSctl::FSctl(const int cmd, rc = SFS_ERROR; } + if (!strcmp(xeq, "cached")) + { + const char* path = args.ArgP[0]; + int rval = myCache.LocalFilePath(path, nullptr, 0, XrdOucCache::LFP_Reason::ForInfo); + if (rval == 0 || rval == -EREMOTE) + { + rc = SFS_OK; + ec = 0; + } + else + { + ec = ETIME; + rc = SFS_ERROR; + TRACE(Info,"Cache "< Auth ID mapper static const char *ofslclCGI = "ofs.lcl=1"; @@ -187,6 +191,12 @@ int XrdPssSys::Init(XrdSysLogger *lp, const char *cFN, XrdOucEnv *envP) tmp = ((NoGo = Configure(cFN, envP)) ? "failed." : "completed."); eDest.Say("------ Proxy storage system initialization ", tmp); +// Extract Pfc control, if it is there. +// + if (!NoGo) + cacheFSctl = (XrdOfsFSctl_PI*)envP->GetPtr("XrdFSCtl_PC*"); + + // All done. // return NoGo; @@ -767,6 +777,28 @@ int XrdPssFile::Open(const char *path, int Oflag, mode_t Mode, XrdOucEnv &Env) } } + // check CGI cache-control paramters + if (cacheFSctl) + { + int elen; + char *envcgi = (char *)Env.Env(elen); + + if (envcgi && strstr(envcgi, "only-if-cached")) + { + XrdOucErrInfo einfo; + XrdSfsFSctl myData; + myData.Arg1 = "cached"; + myData.Arg1Len = 1; + myData.Arg2Len = 1; + const char *myArgs[1]; + myArgs[0] = path; + myData.ArgP = myArgs; + int fsctlRes = cacheFSctl->FSctl(SFS_FSCTL_PLUGXC, myData, einfo); + if (fsctlRes == SFS_ERROR) + return -einfo.getErrInfo(); + } + } + // If this is a third party copy open, then strange rules apply. If this is an // outgoing proxy we let everything pass through as this may be a TPC request // elsewhere. Otherwise, if it's an open for reading, we open the file but From 285951c689a4e62c4a0008e247e067e619b69708 Mon Sep 17 00:00:00 2001 From: alja Date: Fri, 13 Oct 2023 09:56:05 -0700 Subject: [PATCH 002/276] Return a success status only if the file is completely cached --- src/XrdPfc/XrdPfcFSctl.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/XrdPfc/XrdPfcFSctl.cc b/src/XrdPfc/XrdPfcFSctl.cc index b86622bbb52..7cbcd3f36e1 100644 --- a/src/XrdPfc/XrdPfcFSctl.cc +++ b/src/XrdPfc/XrdPfcFSctl.cc @@ -136,7 +136,7 @@ int XrdPfcFSctl::FSctl(const int cmd, { const char* path = args.ArgP[0]; int rval = myCache.LocalFilePath(path, nullptr, 0, XrdOucCache::LFP_Reason::ForInfo); - if (rval == 0 || rval == -EREMOTE) + if (rval == 0) { rc = SFS_OK; ec = 0; From e209717eac58fdbd571f7872abcb1e87f0ef039f Mon Sep 17 00:00:00 2001 From: alja Date: Thu, 26 Oct 2023 21:32:09 -0700 Subject: [PATCH 003/276] Add minsize and minfrac configuration options to tune only-if-cached decisions. --- src/XrdPfc/XrdPfc.cc | 98 +++++++++++++++++++++++++++++++ src/XrdPfc/XrdPfc.hh | 9 +++ src/XrdPfc/XrdPfcConfiguration.cc | 55 ++++++++++++++++- src/XrdPfc/XrdPfcFSctl.cc | 2 +- 4 files changed, 160 insertions(+), 4 deletions(-) diff --git a/src/XrdPfc/XrdPfc.cc b/src/XrdPfc/XrdPfc.cc index ab0fbcbf114..bbbcb52909b 100644 --- a/src/XrdPfc/XrdPfc.cc +++ b/src/XrdPfc/XrdPfc.cc @@ -896,6 +896,104 @@ int Cache::LocalFilePath(const char *curl, char *buff, int blen, return -ENOENT; } +//______________________________________________________________________________ +// Check if the file is cached including m_onlyIfCachedMinSize and m_onlyIfCachedMinFrac +// pfc configuration parameters. The logic of accessing the Info file is the same +// as in Cache::LocalFilePath. +//! @return 0 - the file is complete and the local path to the file is in +//! the buffer, if it has been supllied. +//! +//! @return <0 - the request could not be fulfilled. The return value is +//! -errno describing why. If a buffer was supplied and a +//! path could be generated it is returned only if "why" is +//! ForCheck or ForInfo. Otherwise, a null path is returned. +//! +//! @return >0 - Reserved for future use. +//------------------------------------------------------------------------------ +int Cache::ConsiderCached(const char *curl) +{ + TRACE(Debug, "ConsiderFileCached '" << curl << "'" ); + + XrdCl::URL url(curl); + std::string f_name = url.GetPath(); + std::string i_name = f_name + Info::s_infoExtension; + + { + XrdSysCondVarHelper lock(&m_active_cond); + m_purge_delay_set.insert(f_name); + } + + struct stat sbuff, sbuff2; + if (m_oss->Stat(f_name.c_str(), &sbuff) == XrdOssOK && + m_oss->Stat(i_name.c_str(), &sbuff2) == XrdOssOK) + { + if (S_ISDIR(sbuff.st_mode)) + { + TRACE(Info, "ConsiderCached '" << curl << ", why=ForInfo" << " -> EISDIR"); + return -EISDIR; + } + else + { + bool read_ok = false; + bool is_cached = false; + + // Lock and check if the file is active. If NOT, keep the lock + // and add dummy access after successful reading of info file. + // If it IS active, just release the lock, this ongoing access will + // assure the file continues to exist. + + // XXXX How can I just loop over the cinfo file when active? + // Can I not get is_complete from the existing file? + // Do I still want to inject access record? + // Oh, it writes only if not active .... still let's try to use existing File. + + m_active_cond.Lock(); + + bool is_active = m_active.find(f_name) != m_active.end(); + + if (is_active) + m_active_cond.UnLock(); + + XrdOssDF *infoFile = m_oss->newFile(m_configuration.m_username.c_str()); + XrdOucEnv myEnv; + int res = infoFile->Open(i_name.c_str(), O_RDWR, 0600, myEnv); + if (res >= 0) + { + Info info(m_trace, 0); + if (info.Read(infoFile, i_name.c_str())) + { + read_ok = true; + + if (info.IsComplete()) + { + is_cached = true; + } + else + { + long long fileSize = info.GetFileSize(); + long long bytesRead = info.GetNDownloadedBytes(); + if (bytesRead > m_configuration.m_onlyIfCachedMinSize && + (float)bytesRead/fileSize > m_configuration.m_onlyIfCachedMinFrac) + is_cached = true; + } + } + infoFile->Close(); + } + delete infoFile; + + if (!is_active) m_active_cond.UnLock(); + + if (read_ok) + { + TRACE(Info, "ConsiderCached '" << curl << "', why=ForInfo" << (is_cached ? " -> FILE_COMPLETE_IN_CACHE" : " -> EREMOTE")); + return is_cached ? 0 : -EREMOTE; + } + } + } + + TRACE(Info, "ConsiderCached '" << curl << "', why=ForInfo" << " -> ENOENT"); + return -ENOENT; +} //______________________________________________________________________________ //! Preapare the cache for a file open request. This method is called prior to diff --git a/src/XrdPfc/XrdPfc.hh b/src/XrdPfc/XrdPfc.hh index 6d009c81c8e..fdc4093dd70 100644 --- a/src/XrdPfc/XrdPfc.hh +++ b/src/XrdPfc/XrdPfc.hh @@ -111,6 +111,9 @@ struct Configuration time_t m_cs_UVKeep; //!< unverified checksum cache keep int m_cs_Chk; //!< Checksum check bool m_cs_ChkTLS; //!< Allow TLS + + long long m_onlyIfCachedMinSize; //!< minumum size of downloaded file, used by only-if-cached CGI option + double m_onlyIfCachedMinFrac; //!< minimum fraction of downloaded file, used by only-if-cached CGI option }; //------------------------------------------------------------------------------ @@ -291,6 +294,12 @@ public: // virtual function of XrdOucCache. virtual int Unlink(const char *url); + //--------------------------------------------------------------------- + // Used by PfcFstcl::Fsctl function. + // Test if file is cached taking in onlyifcached configuration parameters. + //--------------------------------------------------------------------- + virtual int ConsiderCached(const char *url); + //-------------------------------------------------------------------- //! \brief Makes decision if the original XrdOucCacheIO should be cached. //! diff --git a/src/XrdPfc/XrdPfcConfiguration.cc b/src/XrdPfc/XrdPfcConfiguration.cc index 0dcb4f5e5ee..8f4bcd064ed 100644 --- a/src/XrdPfc/XrdPfcConfiguration.cc +++ b/src/XrdPfc/XrdPfcConfiguration.cc @@ -46,7 +46,9 @@ Configuration::Configuration() : m_flushCnt(2000), m_cs_UVKeep(-1), m_cs_Chk(CSChk_Net), - m_cs_ChkTLS(false) + m_cs_ChkTLS(false), + m_onlyIfCachedMinSize(1024*1024), + m_onlyIfCachedMinFrac(1.0) {} @@ -488,7 +490,9 @@ bool Cache::Config(const char *config_filename, const char *parameters) " pfc.spaces %s %s\n" " pfc.trace %d\n" " pfc.flush %lld\n" - " pfc.acchistorysize %d\n", + " pfc.acchistorysize %d\n" + " pfc.onlyIfCachedMinBytes %lld\n" + " pfc.onlyIfCachedMinFrac %.2f\n", config_filename, csc[int(m_configuration.m_cs_Chk)], uvk, m_configuration.m_bufferSize, @@ -503,7 +507,9 @@ bool Cache::Config(const char *config_filename, const char *parameters) m_configuration.m_meta_space.c_str(), m_trace->What, m_configuration.m_flushCnt, - m_configuration.m_accHistorySize); + m_configuration.m_accHistorySize, + m_configuration.m_onlyIfCachedMinSize, + m_configuration.m_onlyIfCachedMinFrac); if (m_configuration.is_dir_stat_reporting_on()) { @@ -825,6 +831,49 @@ bool Cache::ConfigParameters(std::string part, XrdOucStream& config, TmpConfigur return false; } } + else if ( part == "onlyifcached" ) + { + const char *p = 0; + while ((p = cwg.GetWord()) && cwg.HasLast()) + { + if (strcmp(p, "minsize") == 0) + { + std::string minBytes = cwg.GetWord(); + long long minBytesTop = 1024 * 1024 * 1024; + if (::isalpha(*(minBytes.rbegin()))) + { + if (XrdOuca2x::a2sz(m_log, "Error in parsing minsize value for onlyifcached parameter", minBytes.c_str(), &m_configuration.m_onlyIfCachedMinSize, 0, minBytesTop)) + { + return false; + } + } + else + { + if (XrdOuca2x::a2ll(m_log, "Error in parsing numeric minsize value for onlyifcached parameter", minBytes.c_str(),&m_configuration.m_onlyIfCachedMinSize, 0, minBytesTop)) + { + return false; + } + } + } + if (strcmp(p, "minfrac") == 0) + { + std::string minFrac = cwg.GetWord(); + char *eP; + errno = 0; + double frac = strtod(minFrac.c_str(), &eP); + if (errno || eP == minFrac.c_str()) + { + m_log.Emsg("Config", "Error setting fraction for only-if-cached directive"); + return false; + } + m_configuration.m_onlyIfCachedMinFrac = frac; + } + else + { + m_log.Emsg("Config", "Error: onlyifcached stanza contains unknown directive", p); + } + } + } else { m_log.Emsg("ConfigParameters() unmatched pfc parameter", part.c_str()); diff --git a/src/XrdPfc/XrdPfcFSctl.cc b/src/XrdPfc/XrdPfcFSctl.cc index 7cbcd3f36e1..2298bfd75b5 100644 --- a/src/XrdPfc/XrdPfcFSctl.cc +++ b/src/XrdPfc/XrdPfcFSctl.cc @@ -135,7 +135,7 @@ int XrdPfcFSctl::FSctl(const int cmd, if (!strcmp(xeq, "cached")) { const char* path = args.ArgP[0]; - int rval = myCache.LocalFilePath(path, nullptr, 0, XrdOucCache::LFP_Reason::ForInfo); + int rval = myCache.ConsiderCached(path); if (rval == 0) { rc = SFS_OK; From a1506f28c988e7f10bb72e91ec5d4c412f68a87e Mon Sep 17 00:00:00 2001 From: alja Date: Mon, 30 Oct 2023 11:50:02 -0700 Subject: [PATCH 004/276] In cache::ConsiderCached handle case where file is zero size and file size is smaller than pfc.onlyifcached minsize --- src/XrdPfc/XrdPfc.cc | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/XrdPfc/XrdPfc.cc b/src/XrdPfc/XrdPfc.cc index bbbcb52909b..d3766daee64 100644 --- a/src/XrdPfc/XrdPfc.cc +++ b/src/XrdPfc/XrdPfc.cc @@ -968,13 +968,26 @@ int Cache::ConsiderCached(const char *curl) { is_cached = true; } + else if (info.GetFileSize() == 0) + { + is_cached = true; + } else { long long fileSize = info.GetFileSize(); long long bytesRead = info.GetNDownloadedBytes(); - if (bytesRead > m_configuration.m_onlyIfCachedMinSize && - (float)bytesRead/fileSize > m_configuration.m_onlyIfCachedMinFrac) + + if (fileSize < m_configuration.m_onlyIfCachedMinSize) + { + if ((float)bytesRead / fileSize > m_configuration.m_onlyIfCachedMinFrac) is_cached = true; + } + else + { + if (bytesRead > m_configuration.m_onlyIfCachedMinSize && + (float)bytesRead / fileSize > m_configuration.m_onlyIfCachedMinFrac) + is_cached = true; + } } } infoFile->Close(); From 28b6256535f6b9432d41b46290cb721531488686 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 5 Sep 2023 11:59:00 +0200 Subject: [PATCH 005/276] [XrdCrypto,XrdSecgsi] Update min/default RSA bits to 2048 --- docs/man/xrdgsiproxy.1 | 2 +- src/XrdCrypto/XrdCryptoAux.hh | 4 ++-- src/XrdCrypto/XrdCryptoFactory.hh | 2 +- src/XrdCrypto/XrdCryptosslRSA.cc | 4 ++-- src/XrdCrypto/XrdCryptosslgsiAux.cc | 8 ++++---- src/XrdSecgsi/XrdSecProtocolgsi.cc | 6 +++--- src/XrdSecgsi/XrdSecProtocolgsi.hh | 4 ++-- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/man/xrdgsiproxy.1 b/docs/man/xrdgsiproxy.1 index c63517a69ab..3f9a10e69ea 100644 --- a/docs/man/xrdgsiproxy.1 +++ b/docs/man/xrdgsiproxy.1 @@ -41,7 +41,7 @@ Non-standard location of certificate for which proxies are wanted Non-standard location of private key to be used to sign the proxy .TP \fB-bits\fR \fIn\fR -Strength in bits of the key [default 512] +Strength in bits of the key [default 2048] .TP \fB-valid\fR \fIhh:mm\fR Time validity of the proxy certificate [default 12:00] diff --git a/src/XrdCrypto/XrdCryptoAux.hh b/src/XrdCrypto/XrdCryptoAux.hh index cbc810ff7ff..d3fa90c082a 100644 --- a/src/XrdCrypto/XrdCryptoAux.hh +++ b/src/XrdCrypto/XrdCryptoAux.hh @@ -49,8 +49,8 @@ #define cryptoTRACE_Notify 0x0001 // RSA parameters -#define XrdCryptoMinRSABits 512 -#define XrdCryptoDefRSABits 1024 +#define XrdCryptoMinRSABits 2048 +#define XrdCryptoDefRSABits 2048 #define XrdCryptoDefRSAExp 0x10001 /******************************************************************************/ diff --git a/src/XrdCrypto/XrdCryptoFactory.hh b/src/XrdCrypto/XrdCryptoFactory.hh index 20c2e1096fb..4d8e5a9d379 100644 --- a/src/XrdCrypto/XrdCryptoFactory.hh +++ b/src/XrdCrypto/XrdCryptoFactory.hh @@ -98,7 +98,7 @@ typedef bool (*XrdCryptoProxyCertInfo_t)(const void *, int &, bool *); typedef void (*XrdCryptoSetPathLenConstraint_t)(void *, int); // create a proxy certificate typedef struct { - int bits; // Number of bits in the RSA key [512] + int bits; // Number of bits in the RSA key [2048] int valid; // Duration validity in secs [43200 (12 hours)] int depthlen; // Maximum depth of the path of proxy certificates // that can signed by this proxy certificates diff --git a/src/XrdCrypto/XrdCryptosslRSA.cc b/src/XrdCrypto/XrdCryptosslRSA.cc index 54818139ac3..ae49406d61d 100644 --- a/src/XrdCrypto/XrdCryptosslRSA.cc +++ b/src/XrdCrypto/XrdCryptosslRSA.cc @@ -88,7 +88,7 @@ XrdCryptosslRSA::XrdCryptosslRSA(int bits, int exp) { // Constructor // Generate a RSA asymmetric key pair - // Length will be 'bits' bits (min 512, default 1024), public + // Length will be 'bits' bits (min 2048, default 2048), public // exponent `pubex` (default 65537). EPNAME("RSA::XrdCryptosslRSA"); @@ -96,7 +96,7 @@ XrdCryptosslRSA::XrdCryptosslRSA(int bits, int exp) prilen = -1; // Minimum is XrdCryptoMinRSABits - bits = (bits >= XrdCryptoMinRSABits) ? bits : XrdCryptoMinRSABits; + bits = (bits >= XrdCryptoMinRSABits) ? bits : XrdCryptoDefRSABits; // If pubex is not odd, use default if (!(exp & 1)) diff --git a/src/XrdCrypto/XrdCryptosslgsiAux.cc b/src/XrdCrypto/XrdCryptosslgsiAux.cc index a88dbd900b8..98548a30b88 100644 --- a/src/XrdCrypto/XrdCryptosslgsiAux.cc +++ b/src/XrdCrypto/XrdCryptosslgsiAux.cc @@ -286,7 +286,7 @@ int XrdCryptosslX509CreateProxy(const char *fnc, const char *fnk, ERR_load_crypto_strings(); // Use default options, if not specified - int bits = (pxopt && pxopt->bits >= 512) ? pxopt->bits : 512; + int bits = (pxopt && pxopt->bits >= XrdCryptoMinRSABits) ? pxopt->bits : XrdCryptoDefRSABits; int valid = (pxopt) ? pxopt->valid : 43200; // 12 hours int depthlen = (pxopt) ? pxopt->depthlen : -1; // unlimited @@ -696,13 +696,13 @@ int XrdCryptosslX509CreateProxyReq(XrdCryptoX509 *xcpi, return -kErrPX_NoResources; } // - // Use same num of bits as the signing certificate, but - // less than 512 + // Use same num of bits as the signing certificate, + // but no less than the minimum RSA bits (2048) ekro.reset(X509_get_pubkey(xpi)); int bits = EVP_PKEY_bits(ekro.get()); ekro = nullptr; - bits = (bits < 512) ? 512 : bits; + bits = (bits < XrdCryptoMinRSABits) ? XrdCryptoDefRSABits : bits; // // Create the new PKI for the proxy (exponent 65537) BIGNUM *e = BN_new(); diff --git a/src/XrdSecgsi/XrdSecProtocolgsi.cc b/src/XrdSecgsi/XrdSecProtocolgsi.cc index 562543d4523..ff067e9775f 100644 --- a/src/XrdSecgsi/XrdSecProtocolgsi.cc +++ b/src/XrdSecgsi/XrdSecProtocolgsi.cc @@ -149,7 +149,7 @@ String XrdSecProtocolgsi::UsrCert = "/.globus/usercert.pem"; String XrdSecProtocolgsi::UsrKey = "/.globus/userkey.pem"; String XrdSecProtocolgsi::PxyValid = "12:00"; int XrdSecProtocolgsi::DepLength= 0; -int XrdSecProtocolgsi::DefBits = 512; +int XrdSecProtocolgsi::DefBits = XrdCryptoDefRSABits; int XrdSecProtocolgsi::CACheck = caVerifyss; int XrdSecProtocolgsi::CRLCheck = crlTry; // 1 int XrdSecProtocolgsi::CRLDownload = 0; @@ -2414,7 +2414,7 @@ char *XrdSecProtocolgsiInit(const char mode, // ["12:00", i.e. 12 hours] // "XrdSecGSIPROXYDEPLEN" depth of signature path for proxies; // use -1 for unlimited [0] - // "XrdSecGSIPROXYKEYBITS" bits in PKI for proxies [512] + // "XrdSecGSIPROXYKEYBITS" bits in PKI for proxies [default: XrdCryptoDefRSABits] // "XrdSecGSICACHECK" CA check level [1]: // 0 do not verify; // 1 verify if self-signed, warn if not; @@ -4889,7 +4889,7 @@ int XrdSecProtocolgsi::InitProxy(ProxyIn_t *pi, XrdCryptoFactory *cf, X509Chain } // Add number of bits in key - if (pi->bits > 512) { + if (pi->bits != XrdCryptoDefRSABits) { cmd += " -bits "; cmd += pi->bits; } diff --git a/src/XrdSecgsi/XrdSecProtocolgsi.hh b/src/XrdSecgsi/XrdSecProtocolgsi.hh index 2c79f5aebe8..6c0f4fca1f3 100644 --- a/src/XrdSecgsi/XrdSecProtocolgsi.hh +++ b/src/XrdSecgsi/XrdSecProtocolgsi.hh @@ -188,7 +188,7 @@ public: char *proxy; // [c] user proxy [/tmp/x509up_u] char *valid; // [c] proxy validity [12:00] int deplen; // [c] depth of signature path for proxies [0] - int bits; // [c] bits in PKI for proxies [512] + int bits; // [c] bits in PKI for proxies [default: XrdCryptoDefRSABits] char *gridmap;// [s] gridmap file [/etc/grid-security/gridmap] int gmapto; // [s] validity in secs of grid-map cache entries [600 s] char *gmapfun;// [s] file with the function to map DN to usernames [0] @@ -218,7 +218,7 @@ public: gsiOptions() { debug = -1; mode = 's'; clist = 0; certdir = 0; crldir = 0; crlext = 0; cert = 0; key = 0; cipher = 0; md = 0; ca = 1 ; crl = 1; crlrefresh = 86400; - proxy = 0; valid = 0; deplen = 0; bits = 512; + proxy = 0; valid = 0; deplen = 0; bits = XrdCryptoDefRSABits; gridmap = 0; gmapto = 600; gmapfun = 0; gmapfunparms = 0; authzfun = 0; authzfunparms = 0; authzto = -1; authzcall = 1; From 9ef3a2a00b52105883613d2adb6d46a8409b2249 Mon Sep 17 00:00:00 2001 From: Chris Green Date: Wed, 22 Feb 2023 08:27:07 -0600 Subject: [PATCH 006/276] Make CMAKE_CXX_STANDARD a CACHE variable This allows CMAKE_CXX_STANDARD to be customized from the CMake CLI with (e.g.) `-DCMAKE_CXX_STANDARD=17`. --- cmake/XRootDOSDefs.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/XRootDOSDefs.cmake b/cmake/XRootDOSDefs.cmake index 475fcfe33da..5f1ebc71546 100644 --- a/cmake/XRootDOSDefs.cmake +++ b/cmake/XRootDOSDefs.cmake @@ -18,7 +18,7 @@ define_default( LIBRARY_PATH_PREFIX "lib" ) #------------------------------------------------------------------------------- # Enable c++14 #------------------------------------------------------------------------------- -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 14 CACHE STRING "C++ Standard") set(CMAKE_CXX_STANDARD_REQUIRED TRUE) if( ENABLE_ASAN ) From 22247391468328ca6a73697177dc0cf0a95528cb Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Thu, 23 Mar 2023 10:57:30 +0100 Subject: [PATCH 007/276] Remove 'using namespace std;' from headers and source files This is necessary to avoid name clashes between the std and global namespaces. It was identified as a problem when trying to compile XRootD with C++17 standard and support for VOMS enabled, since VOMS has a struct named 'struct data' in the global namespace, which clashes with std::data from C++17. --- src/XrdApps/XrdCpConfig.cc | 1 - src/XrdApps/XrdCrc32c.cc | 1 - src/XrdApps/XrdMpxXml.cc | 1 - src/XrdApps/XrdQStats.cc | 1 - src/XrdNet/XrdNetIF.cc | 1 - src/XrdOuc/XrdOucNSWalk.cc | 1 - src/XrdOuc/XrdOucString.hh | 2 -- src/XrdSciTokens/vendor/picojson/examples/github-issues.cc | 1 - src/XrdSciTokens/vendor/picojson/test.cc | 1 - src/XrdSsi/XrdSsiLogging.cc | 1 - src/XrdSsi/XrdSsiShMam.cc | 1 - src/XrdSys/XrdSysHeaders.hh | 1 - src/XrdSys/XrdSysIOEventsPollE.icc | 1 - src/XrdSys/XrdSysIOEventsPollKQ.icc | 1 - src/XrdSys/XrdSysIOEventsPollPoll.icc | 1 - src/XrdSys/XrdSysIOEventsPollPort.icc | 1 - tests/XrdSsiTests/XrdShMap.cc | 1 - 17 files changed, 18 deletions(-) diff --git a/src/XrdApps/XrdCpConfig.cc b/src/XrdApps/XrdCpConfig.cc index 5981c4a9242..890f80198cc 100644 --- a/src/XrdApps/XrdCpConfig.cc +++ b/src/XrdApps/XrdCpConfig.cc @@ -47,7 +47,6 @@ #include "XrdSys/XrdSysHeaders.hh" #include "XrdSys/XrdSysLogger.hh" -using namespace std; /******************************************************************************/ /* D e f i n e M a c r o s */ diff --git a/src/XrdApps/XrdCrc32c.cc b/src/XrdApps/XrdCrc32c.cc index 0b5f26de86d..b213d6dc230 100644 --- a/src/XrdApps/XrdCrc32c.cc +++ b/src/XrdApps/XrdCrc32c.cc @@ -42,7 +42,6 @@ #include "XrdOuc/XrdOucCRC.hh" #include "XrdSys/XrdSysE2T.hh" -using namespace std; namespace {const char *pgm = "xrdcrc32c"; diff --git a/src/XrdApps/XrdMpxXml.cc b/src/XrdApps/XrdMpxXml.cc index 22284b98cdc..a8840c848cd 100644 --- a/src/XrdApps/XrdMpxXml.cc +++ b/src/XrdApps/XrdMpxXml.cc @@ -40,7 +40,6 @@ #include "XrdApps/XrdMpxXml.hh" #include "XrdOuc/XrdOucTokenizer.hh" -using namespace std; /******************************************************************************/ /* v n M a p D e f i n i t i o n */ diff --git a/src/XrdApps/XrdQStats.cc b/src/XrdApps/XrdQStats.cc index aa64ae85102..65263fd9e69 100644 --- a/src/XrdApps/XrdQStats.cc +++ b/src/XrdApps/XrdQStats.cc @@ -40,7 +40,6 @@ #include "XrdCl/XrdClURL.hh" #include "XrdCl/XrdClXRootDResponses.hh" -using namespace std; /******************************************************************************/ /* F a t a l */ diff --git a/src/XrdNet/XrdNetIF.cc b/src/XrdNet/XrdNetIF.cc index e7c0a51fbe5..5e06b79da12 100644 --- a/src/XrdNet/XrdNetIF.cc +++ b/src/XrdNet/XrdNetIF.cc @@ -47,7 +47,6 @@ #include "XrdSys/XrdSysError.hh" #include -using namespace std; /******************************************************************************/ /* L o c a l S t a t i c s */ diff --git a/src/XrdOuc/XrdOucNSWalk.cc b/src/XrdOuc/XrdOucNSWalk.cc index 34ca4accdd9..4904972c0df 100644 --- a/src/XrdOuc/XrdOucNSWalk.cc +++ b/src/XrdOuc/XrdOucNSWalk.cc @@ -40,7 +40,6 @@ #include "XrdSys/XrdSysHeaders.hh" #include "XrdSys/XrdSysPlatform.hh" -using namespace std; /******************************************************************************/ /* C o n s t r u c t o r */ diff --git a/src/XrdOuc/XrdOucString.hh b/src/XrdOuc/XrdOucString.hh index b4f3c1f79d0..f463f3be87f 100644 --- a/src/XrdOuc/XrdOucString.hh +++ b/src/XrdOuc/XrdOucString.hh @@ -247,8 +247,6 @@ #include #include -using namespace std; - #define STR_NPOS -1 class XrdOucString { diff --git a/src/XrdSciTokens/vendor/picojson/examples/github-issues.cc b/src/XrdSciTokens/vendor/picojson/examples/github-issues.cc index 4bfb15b12ac..2ac93a2cba2 100644 --- a/src/XrdSciTokens/vendor/picojson/examples/github-issues.cc +++ b/src/XrdSciTokens/vendor/picojson/examples/github-issues.cc @@ -67,7 +67,6 @@ char *memfstrdup(MEMFILE *mf) { return buf; } -using namespace std; using namespace picojson; int main(int argc, char *argv[]) { diff --git a/src/XrdSciTokens/vendor/picojson/test.cc b/src/XrdSciTokens/vendor/picojson/test.cc index 582a29a259c..9cb2dce3336 100644 --- a/src/XrdSciTokens/vendor/picojson/test.cc +++ b/src/XrdSciTokens/vendor/picojson/test.cc @@ -32,7 +32,6 @@ #pragma warning(disable : 4127) // conditional expression is constant #endif -using namespace std; #define is(x, y, name) _ok((x) == (y), name) diff --git a/src/XrdSsi/XrdSsiLogging.cc b/src/XrdSsi/XrdSsiLogging.cc index 9241f58a1f9..0a8e127a424 100644 --- a/src/XrdSsi/XrdSsiLogging.cc +++ b/src/XrdSsi/XrdSsiLogging.cc @@ -51,7 +51,6 @@ namespace XrdSsi extern XrdSsiLogger::MCB_t *msgCB; } -using namespace std; using namespace XrdSsi; /******************************************************************************/ diff --git a/src/XrdSsi/XrdSsiShMam.cc b/src/XrdSsi/XrdSsiShMam.cc index fdd70e14309..be6f8e94022 100644 --- a/src/XrdSsi/XrdSsiShMam.cc +++ b/src/XrdSsi/XrdSsiShMam.cc @@ -44,7 +44,6 @@ #include "XrdSsi/XrdSsiShMam.hh" #include "XrdSys/XrdSysE2T.hh" -using namespace std; /* Gentoo removed OF from their copy of zconf.h but we need it here. See https://bugs.gentoo.org/show_bug.cgi?id=383179 for the sad history. diff --git a/src/XrdSys/XrdSysHeaders.hh b/src/XrdSys/XrdSysHeaders.hh index 88230fd5c65..62dcc23a9ad 100644 --- a/src/XrdSys/XrdSysHeaders.hh +++ b/src/XrdSys/XrdSysHeaders.hh @@ -38,7 +38,6 @@ // gcc >= 4.3, cl require this # include -using namespace std; #else diff --git a/src/XrdSys/XrdSysIOEventsPollE.icc b/src/XrdSys/XrdSysIOEventsPollE.icc index 12aa3d742fa..eb9cb8f296e 100644 --- a/src/XrdSys/XrdSysIOEventsPollE.icc +++ b/src/XrdSys/XrdSysIOEventsPollE.icc @@ -39,7 +39,6 @@ #define Atomic(x) x #endif -using namespace std; /******************************************************************************/ /* C l a s s P o l l E */ diff --git a/src/XrdSys/XrdSysIOEventsPollKQ.icc b/src/XrdSys/XrdSysIOEventsPollKQ.icc index abfffb7b0aa..584730b5f7a 100644 --- a/src/XrdSys/XrdSysIOEventsPollKQ.icc +++ b/src/XrdSys/XrdSysIOEventsPollKQ.icc @@ -40,7 +40,6 @@ #define Atomic(x) x #endif -using namespace std; /******************************************************************************/ /* C l a s s P o l l E */ diff --git a/src/XrdSys/XrdSysIOEventsPollPoll.icc b/src/XrdSys/XrdSysIOEventsPollPoll.icc index 9fe74720fc8..38172f27a85 100644 --- a/src/XrdSys/XrdSysIOEventsPollPoll.icc +++ b/src/XrdSys/XrdSysIOEventsPollPoll.icc @@ -37,7 +37,6 @@ #include "Xrd/XrdScheduler.hh" -using namespace std; /******************************************************************************/ /* C l a s s P o l l P o l l */ diff --git a/src/XrdSys/XrdSysIOEventsPollPort.icc b/src/XrdSys/XrdSysIOEventsPollPort.icc index d4059b5d989..f5a4ebd9388 100644 --- a/src/XrdSys/XrdSysIOEventsPollPort.icc +++ b/src/XrdSys/XrdSysIOEventsPollPort.icc @@ -35,7 +35,6 @@ @include "XrdSys/XrdSysE2T.hh" -using namespace std; /******************************************************************************/ /* C l a s s P o l l P o r t */ diff --git a/tests/XrdSsiTests/XrdShMap.cc b/tests/XrdSsiTests/XrdShMap.cc index 709ed8467f8..dfb51aae9d5 100644 --- a/tests/XrdSsiTests/XrdShMap.cc +++ b/tests/XrdSsiTests/XrdShMap.cc @@ -41,7 +41,6 @@ #include "XrdSsi/XrdSsiShMap.hh" -using namespace std; /* Gentoo removed OF from their copy of zconf.h but we need it here. See https://bugs.gentoo.org/show_bug.cgi?id=383179 for the sad history. From d3d39999bc26bc49edc6cb0f2b43305d0a455805 Mon Sep 17 00:00:00 2001 From: Andrew Hanushevsky Date: Fri, 10 Nov 2023 21:34:46 -0800 Subject: [PATCH 008/276] [Server] Add enhanced error message I/F and correct XrdOss:Create() arg passthrough issue This commit combines two previous commits from the 'devel' branch, listed below: - [Server] Pass through missing argument in XrdOss:Create() wrapper (a8f8f3df) - [Server] Commit remaining files/patches for inadvertent commit c29ef89 (3e73eb1b) --- src/XrdFfs/XrdFfsMisc.cc | 7 +- src/XrdOss/XrdOssWrapper.hh | 2 +- src/XrdOuc/XrdOucECMsg.cc | 175 ++++++++++++++++++++++++ src/XrdOuc/XrdOucECMsg.hh | 178 +++++++++++++++++++++++++ src/XrdPosix/XrdPosixAdmin.cc | 12 +- src/XrdPosix/XrdPosixAdmin.hh | 9 +- src/XrdPosix/XrdPosixDir.cc | 10 +- src/XrdPosix/XrdPosixDir.hh | 2 +- src/XrdPosix/XrdPosixExtra.cc | 3 + src/XrdPosix/XrdPosixFile.cc | 43 +++--- src/XrdPosix/XrdPosixFileRH.cc | 3 +- src/XrdPosix/XrdPosixMap.cc | 13 +- src/XrdPosix/XrdPosixMap.hh | 4 +- src/XrdPosix/XrdPosixObject.cc | 9 ++ src/XrdPosix/XrdPosixObject.hh | 7 +- src/XrdPosix/XrdPosixPrepIO.cc | 2 +- src/XrdPosix/XrdPosixXrootd.cc | 236 ++++++++++++++++++++------------- src/XrdPosix/XrdPosixXrootd.hh | 30 ++++- src/XrdPss/XrdPss.cc | 16 ++- src/XrdPss/XrdPss.hh | 2 + src/XrdUtils.cmake | 1 + 21 files changed, 616 insertions(+), 148 deletions(-) create mode 100644 src/XrdOuc/XrdOucECMsg.cc create mode 100644 src/XrdOuc/XrdOucECMsg.hh diff --git a/src/XrdFfs/XrdFfsMisc.cc b/src/XrdFfs/XrdFfsMisc.cc index f4ee7bdbba4..bc971719e83 100644 --- a/src/XrdFfs/XrdFfsMisc.cc +++ b/src/XrdFfs/XrdFfsMisc.cc @@ -44,6 +44,7 @@ #include "XrdNet/XrdNetAddr.hh" #include "XrdNet/XrdNetUtils.hh" +#include "XrdOuc/XrdOucECMsg.hh" #include "XrdPosix/XrdPosixAdmin.hh" #include "XrdSec/XrdSecEntity.hh" #include "XrdSecsss/XrdSecsssID.hh" @@ -75,7 +76,8 @@ char XrdFfsMisc_get_current_url(const char *oldurl, char *newurl) return 1; } - XrdPosixAdmin adm(oldurl); + XrdOucECMsg ecMsg; + XrdPosixAdmin adm(oldurl,ecMsg); if (adm.isOK() && adm.Stat()) { // We might have been redirected to a destination server. Better @@ -100,7 +102,8 @@ char* XrdFfsMisc_getNameByAddr(char* ipaddr) int XrdFfsMisc_get_all_urls_real(const char *oldurl, char **newurls, const int nnodes) { - XrdPosixAdmin adm(oldurl); + XrdOucECMsg ecMsg; + XrdPosixAdmin adm(oldurl,ecMsg); XrdCl::URL *uVec; int i, rval = 0; diff --git a/src/XrdOss/XrdOssWrapper.hh b/src/XrdOss/XrdOssWrapper.hh index 4df3d792330..8658271293f 100644 --- a/src/XrdOss/XrdOssWrapper.hh +++ b/src/XrdOss/XrdOssWrapper.hh @@ -509,7 +509,7 @@ virtual void Connect(XrdOucEnv &env) {wrapPI.Connect(env);} virtual int Create(const char *tid, const char *path, mode_t mode, XrdOucEnv &env, int opts=0) - {return wrapPI.Create(tid, path, mode, env);} + {return wrapPI.Create(tid, path, mode, env, opts);} //----------------------------------------------------------------------------- //! Notify storage system that a client has disconnected. diff --git a/src/XrdOuc/XrdOucECMsg.cc b/src/XrdOuc/XrdOucECMsg.cc new file mode 100644 index 00000000000..6189d40b022 --- /dev/null +++ b/src/XrdOuc/XrdOucECMsg.cc @@ -0,0 +1,175 @@ +/******************************************************************************/ +/* */ +/* X r d O u c E C M s g . c c */ +/* */ +/* (c) 2023 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 "XrdOuc/XrdOucECMsg.hh" +#include "XrdSys/XrdSysE2T.hh" + +/******************************************************************************/ +/* G e t */ +/******************************************************************************/ + +int XrdOucECMsg::Get(std::string& ecm, bool rst) +{ + if (!rst) + {ecm = ecMsg; + return eCode; + } + + int ec = eCode; + eCode = 0; + ecm = std::move(ecMsg); + ecMsg.erase(); + return ec; +} + +/******************************************************************************/ +/* M s g */ +/******************************************************************************/ + +void XrdOucECMsg::Msg(const char *pfx, const char *txt1, + const char *txt2, const char *txt3, + const char *txt4, const char *txt5) +{ + + const char *vecP[10]; + int n = 0; + bool xSpace = false; + + if (txt1 && *txt1) {vecP[n++] = txt1; xSpace = true;} + if (txt2 && *txt2) {if (xSpace) vecP[n++] = " "; + vecP[n++] = txt2; xSpace = true; + } + if (txt3 && *txt3) {if (xSpace) vecP[n++] = " "; + vecP[n++] = txt3; xSpace = true; + } + if (txt4 && *txt4) {if (xSpace) vecP[n++] = " "; + vecP[n++] = txt4; xSpace = true; + } + if (txt5 && *txt5) {if (xSpace) vecP[n++] = " "; + vecP[n++] = txt5; + } + +// Route the message appropriately +// + MsgVec(pfx, vecP, n); +} + +/******************************************************************************/ +/* M s g f */ +/******************************************************************************/ + +void XrdOucECMsg::Msgf(const char *pfx, const char *fmt, ...) +{ + char buffer[2048]; + va_list args; + va_start (args, fmt); + +// Format the message +// + int n = vsnprintf(buffer, sizeof(buffer), fmt, args); + +// Append as needed +// + if (n > (int)sizeof(buffer)) n = sizeof(buffer); + Setup(pfx, n); + ecMsg.append(buffer); +} + +/******************************************************************************/ +/* M s g V A */ +/******************************************************************************/ + +void XrdOucECMsg::MsgVA(const char *pfx, const char *fmt, va_list aP) +{ + char buffer[2048]; + +// Format the message +// + int n = vsnprintf(buffer, sizeof(buffer), fmt, aP); + +// Append as needed +// + if (n > (int)sizeof(buffer)) n = sizeof(buffer); + Setup(pfx, n); + ecMsg.append(buffer); +} + +/******************************************************************************/ +/* M s g V e c */ +/******************************************************************************/ + +void XrdOucECMsg::MsgVec(const char* pfx, char const* const* vecP, int vecN) +{ + int n = 0; + + for (int i = 0; i < vecN; i++) n += strlen(vecP[i]); + Setup(pfx, n); + for (int i = 0; i < vecN; i++) ecMsg.append(vecP[i]); +} + +/******************************************************************************/ +/* S e t E r r n o */ +/******************************************************************************/ + +int XrdOucECMsg::SetErrno(int ecc, int ret, const char *alt) +{ + if (!alt || *alt != '*') + {if (!msgID) ecMsg = (alt ? alt : XrdSysE2T(ecc)); + else Msgf(msgID, XrdSysE2T(ecc)); + } + errno = eCode = ecc; + return ret; +} + +/******************************************************************************/ +/* S e t u p */ +/******************************************************************************/ + +void XrdOucECMsg::Setup(const char* pfx, int n) +{ + int k = (pfx && *pfx ? strlen(pfx)+2 : 0); + + if (Delim) + {ecMsg.reserve(ecMsg.length() + n + k + 2); + ecMsg.append(&Delim, 1); + Delim = 0; + } else { + ecMsg.reserve(n + k + 1); + ecMsg = ""; + } + + if (k) + {ecMsg.append(pfx); + ecMsg.append(": "); + } +} diff --git a/src/XrdOuc/XrdOucECMsg.hh b/src/XrdOuc/XrdOucECMsg.hh new file mode 100644 index 00000000000..b29c40e78d0 --- /dev/null +++ b/src/XrdOuc/XrdOucECMsg.hh @@ -0,0 +1,178 @@ +#ifndef __OUC_ECMSG_H__ +#define __OUC_ECMSG_H__ +/******************************************************************************/ +/* */ +/* X r d O u c E C M s g . h h */ +/* */ +/* (c) 2023 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 + +class XrdOucECMsg +{ +public: + +//----------------------------------------------------------------------------- +//! Append subsequent message to contents of ecMsg using delimeter. Append +//! allows method chaining for a more natural progamming style. +//! +//! @param dlm !0 -> The character to use as message separator. +//! dlm =0 -> Turns off appending, next message replaces ecMsg. +//! +//! @return Reference to the subject object. +//----------------------------------------------------------------------------- + +XrdOucECMsg& Append(char dlm='\n') {Delim = dlm; return *this;} + +//----------------------------------------------------------------------------- +//! Get err code and error message. +//! +//! @param ecm Reference to std:string where message is to be placed. +//! @param rst When true, the eCode and internal string are set to null. +//! +//! @return the error code, eCode, and associated error message. +//----------------------------------------------------------------------------- + +int Get(std::string& ecm, bool rst=true); + +//----------------------------------------------------------------------------- +//! Determine if an error text message exists. +//! +//! @return true if an error text message exists, false otherwise. +//----------------------------------------------------------------------------- + +bool hasMsg() const {return !ecMsg.empty();} + +//----------------------------------------------------------------------------- +//! Return the message string. +//! +//! @return A reference to the message string. +//----------------------------------------------------------------------------- + +std::string& Msg() {return ecMsg;} + +//----------------------------------------------------------------------------- +//! Insert a space delimited error message into ecMsg string. +//! +//! @param pfx !0 -> the text to prefix the message; the message is formed as +//! pfx: txt1 [txt2] [txt3] +//! pfx =0 -> insert message into ecMsg without a prefix. +//! @param txt1,txt2,txt3,txt4,txt5 the message to be inserted to ecMsg. +//----------------------------------------------------------------------------- + +void Msg(const char* pfx, const char* txt1, + const char* txt2=0, const char* txt3=0, + const char* txt4=0, const char* txt5=0); + +//----------------------------------------------------------------------------- +//! Insert a formated error message into ecMsg using variable args. +//! +//! @param pfx !0 -> the text to prefix the message; the message is formed as +//! : +//! pfx =0 -> insert message without a prefix. +//! @param fmt the message formatting template (i.e. sprintf format). +//! @param ... the arguments that should be used with the template. The +//! formatted message is truncated at 2048 bytes. +//----------------------------------------------------------------------------- + +void Msgf(const char *pfx, const char *fmt, ...); + +//----------------------------------------------------------------------------- +//! Insert a formated error message into the ecMsg using a va_list. +//! +//! @param pfx !0 -> the text to prefix the message; the message is formed as +//! : +//! pfx =0 -> add message to the log without a prefix. +//! @param fmt the message formatting template (i.e. sprintf format). +//! @param aP the arguments that should be used with the template. The +//! formatted message is truncated at 2048 bytes. +//----------------------------------------------------------------------------- + +void MsgVA(const char *pfx, const char *fmt, std::va_list aP); + +//----------------------------------------------------------------------------- +//! Insert a formated error message into ecMsg using an iovec. +//! +//! @param pfx !0 -> the text to prefix the message; the message is formed as +//! pfx: +//! pfx =0 -> insert message into ecMsg without a prefix. +//! @param vecP pointer to a vector strings to insert into the message. +//! Spaces are not inserted between the elements. +//! @param vecN the number of elements in vecP. +//----------------------------------------------------------------------------- + +void MsgVec(const char* pfx, char const* const* vecP, int vecN); + +//----------------------------------------------------------------------------- +//! Set error message and error code. +//! +//! @param ecc The error code. +//! @param ecm The error message, if nil then message is not changed. +//----------------------------------------------------------------------------- + +void Set(int ecc, const char* ecm="") {eCode = ecc; if (ecm) ecMsg = ecm;} + +void Set(int ecc, std::string& ecm) {eCode = ecc; ecMsg = ecm;} + +//----------------------------------------------------------------------------- +//! Set default error message, error code, and errno. +//! +//! @param ecc The error code. +//! @param ret The value to be returned, default -1. +//! @param alt Alternative message, default text of ecc error. +//! +//! @return The ret parameter value is returned. +//----------------------------------------------------------------------------- + +int SetErrno(int ecc, int retval=-1, const char *alt=0); + +//----------------------------------------------------------------------------- +//! Assignment operators for convenience. +//----------------------------------------------------------------------------- + + XrdOucECMsg& operator=(const int rhs) {eCode = rhs; return *this;}; + + XrdOucECMsg& operator=(const std::string& rhs) {ecMsg = rhs; return *this;}; + + XrdOucECMsg& operator=(const char* rhs) {ecMsg = rhs; return *this;}; + + XrdOucECMsg& operator=(XrdOucECMsg& rhs) + {ecMsg = rhs.ecMsg; eCode = rhs.eCode; return *this;}; + + XrdOucECMsg(const char *msgid=0) : msgID(msgid) {} + ~XrdOucECMsg() {} + +private: + +void Setup(const char *pfx, int n); +const char* msgID; +std::string ecMsg; +int eCode = 0; +char Delim = 0; +}; +#endif diff --git a/src/XrdPosix/XrdPosixAdmin.cc b/src/XrdPosix/XrdPosixAdmin.cc index 5e37e72a267..f03f29e295e 100644 --- a/src/XrdPosix/XrdPosixAdmin.cc +++ b/src/XrdPosix/XrdPosixAdmin.cc @@ -61,7 +61,7 @@ XrdCl::URL *XrdPosixAdmin::FanOut(int &num) // xStatus = Xrd.DeepLocate(Url.GetPathWithParams(),XrdCl::OpenFlags::None,info); if (!xStatus.IsOK()) - {num = XrdPosixMap::Result(xStatus, false); + {num = XrdPosixMap::Result(xStatus, ecMsg, false); return 0; } @@ -110,7 +110,7 @@ int XrdPosixAdmin::Query(XrdCl::QueryCode::Code reqCode, void *buff, int bsz) // Issue the query // - if (!XrdPosixMap::Result(Xrd.Query(reqCode, reqBuff, rspBuff))) + if (!XrdPosixMap::Result(Xrd.Query(reqCode, reqBuff, rspBuff),ecMsg)) {uint32_t rspSz = rspBuff->GetSize(); char *rspbuff = rspBuff->GetBuffer(); if (rspbuff && rspSz) @@ -121,8 +121,8 @@ int XrdPosixAdmin::Query(XrdCl::QueryCode::Code reqCode, void *buff, int bsz) ((char*)buff)[rspSz] = 0; // make sure it is null-terminated delete rspBuff; return static_cast(rspSz + 1); - } else errno = ERANGE; - } else errno = EFAULT; + } else ecMsg.SetErrno(ERANGE,0,"buffer to small to hold result"); + } else ecMsg.SetErrno(EFAULT,0,"Invalid return results"); } // Return error @@ -148,7 +148,7 @@ bool XrdPosixAdmin::Stat(mode_t *flags, time_t *mtime) // xStatus = Xrd.Stat(Url.GetPathWithParams(), sInfo); if (!xStatus.IsOK()) - {XrdPosixMap::Result(xStatus); + {XrdPosixMap::Result(xStatus,ecMsg); delete sInfo; return false; } @@ -179,7 +179,7 @@ bool XrdPosixAdmin::Stat(struct stat &Stat) // xStatus = Xrd.Stat(Url.GetPathWithParams(), sInfo); if (!xStatus.IsOK()) - {XrdPosixMap::Result(xStatus); + {XrdPosixMap::Result(xStatus,ecMsg); delete sInfo; return false; } diff --git a/src/XrdPosix/XrdPosixAdmin.hh b/src/XrdPosix/XrdPosixAdmin.hh index 1ced9fda4ac..f56ce54c1bf 100644 --- a/src/XrdPosix/XrdPosixAdmin.hh +++ b/src/XrdPosix/XrdPosixAdmin.hh @@ -36,6 +36,7 @@ #include "XrdCl/XrdClFileSystem.hh" #include "XrdCl/XrdClURL.hh" #include "XrdCl/XrdClXRootDResponses.hh" +#include "XrdOuc/XrdOucECMsg.hh" /******************************************************************************/ /* X r d P o s i x A d m i n */ @@ -49,8 +50,12 @@ public: XrdCl::URL Url; XrdCl::FileSystem Xrd; +XrdOucECMsg& ecMsg; bool isOK() {if (Url.IsValid()) return true; + ecMsg.Set(EINVAL, 0); + ecMsg.Msgf("PosixAdmin", "url '%s' is invalid", + Url.GetURL()); errno = EINVAL; return false; } @@ -62,8 +67,8 @@ bool Stat(mode_t *flags=0, time_t *mtime=0); bool Stat(struct stat &Stat); - XrdPosixAdmin(const char *path) - : Url((std::string)path), Xrd(Url) {} + XrdPosixAdmin(const char *path, XrdOucECMsg &ecm) + : Url((std::string)path), Xrd(Url), ecMsg(ecm) {} ~XrdPosixAdmin() {} }; #endif diff --git a/src/XrdPosix/XrdPosixDir.cc b/src/XrdPosix/XrdPosixDir.cc index 027d670aca0..8d55e34ac77 100644 --- a/src/XrdPosix/XrdPosixDir.cc +++ b/src/XrdPosix/XrdPosixDir.cc @@ -54,7 +54,7 @@ dirent64 *XrdPosixDir::nextEntry(dirent64 *dp) // Reread the directory if we need to (rewind forces this) // - if (!myDirVec && !Open()) {eNum = errno; return 0;} + if (!myDirVec && !Open()) {eNum = errno; return 0;} // Open() sets ecMsg // Check if dir is empty or all entries have been read // @@ -100,15 +100,17 @@ DIR *XrdPosixDir::Open() // some system the dirent structure does not include the name buffer // if (!myDirEnt && !(myDirEnt = (dirent64 *)malloc(dEntSize))) - {errno = ENOMEM; return (DIR *)0;} + {ecMsg.SetErrno(ENOMEM); + return (DIR*)0; + } // Get the directory list // rc = XrdPosixMap::Result(DAdmin.Xrd.DirList(DAdmin.Url.GetPathWithParams(), XrdPosixGlobals::dlFlag, - myDirVec, (uint16_t)0)); + myDirVec, (uint16_t)0),ecMsg); -// If we failed, return a zero pointer +// If we failed, return a zero pointer ote that Result() set errno for us // if (rc) return (DIR *)0; diff --git a/src/XrdPosix/XrdPosixDir.hh b/src/XrdPosix/XrdPosixDir.hh index c7985af7894..be1daee555a 100644 --- a/src/XrdPosix/XrdPosixDir.hh +++ b/src/XrdPosix/XrdPosixDir.hh @@ -49,7 +49,7 @@ class XrdPosixDir : public XrdPosixObject { public: XrdPosixDir(const char *path) - : DAdmin(path), myDirVec(0), myDirEnt(0), + : DAdmin(path,ecMsg), myDirVec(0), myDirEnt(0), nxtEnt(0), numEnt(0), eNum(0) {} diff --git a/src/XrdPosix/XrdPosixExtra.cc b/src/XrdPosix/XrdPosixExtra.cc index a77b3e27c3a..307f1733740 100644 --- a/src/XrdPosix/XrdPosixExtra.cc +++ b/src/XrdPosix/XrdPosixExtra.cc @@ -37,6 +37,8 @@ #include "XrdPosix/XrdPosixExtra.hh" #include "XrdPosix/XrdPosixFile.hh" + + /******************************************************************************/ /* p g R e a d */ /******************************************************************************/ @@ -64,6 +66,7 @@ ssize_t XrdPosixExtra::pgRead (int fildes, void* buffer, // if (rdlen > (size_t)0x7fffffff) {fp->UnLock(); + errno = EOVERFLOW; if (!cbp) return -1; cbp->Complete(-1); diff --git a/src/XrdPosix/XrdPosixFile.cc b/src/XrdPosix/XrdPosixFile.cc index 5a58f7b2f05..09c55f971e3 100644 --- a/src/XrdPosix/XrdPosixFile.cc +++ b/src/XrdPosix/XrdPosixFile.cc @@ -400,11 +400,12 @@ void XrdPosixFile::HandleResponse(XrdCl::XRootDStatus *status, // If no errors occurred, complete the open // - if (!(status->IsOK())) rc = XrdPosixMap::Result(*status); - else if (!Finalize(&Status)) rc = XrdPosixMap::Result(Status); + if (!(status->IsOK())) rc = XrdPosixMap::Result(*status,ecMsg,false); + else if (!Finalize(&Status)) rc = XrdPosixMap::Result( Status,ecMsg,false); -// Issue XrdPosixCallBack callback with the correct result. Note: errors are -// indicated with result set to -1 and errno set to the error number. +// Issue XrdPosixCallBack callback with the correct result. Errors are indicated +// by result set < 0 (typically -1) and errno set to the error number. In our +// case, rc is -errno if an error occured and that is what the callback gets. // xeqCB->Complete(rc); @@ -489,10 +490,10 @@ void XrdPosixFile::pgRead(XrdOucCacheIOCB &iocb, Ref(); Status = clFile.PgRead((uint64_t)offs,(uint32_t)rlen,buff,rhP); -// Check status +// Check status, upon error we pass -errno as the result. // if (!Status.IsOK()) - {rhP->Sched(XrdPosixMap::Result(Status, false)); + {rhP->Sched(XrdPosixMap::Result(Status, ecMsg, false)); unRef(); } } @@ -514,13 +515,13 @@ int XrdPosixFile::pgWrite(char *buff, // if (csfix) *csfix = 0; -// Issue write and return appropriately +// Issue write and return appropriately. An error returns -1. // Ref(); Status = clFile.PgWrite((uint64_t)offs, (uint32_t)wlen, buff, csvec); unRef(); - return (Status.IsOK() ? wlen : XrdPosixMap::Result(Status)); + return (Status.IsOK() ? wlen : XrdPosixMap::Result(Status,ecMsg,true)); } /******************************************************************************/ @@ -553,10 +554,10 @@ void XrdPosixFile::pgWrite(XrdOucCacheIOCB &iocb, Ref(); Status = clFile.PgWrite((uint64_t)offs, (uint32_t)wlen, buff, csvec, rhP); -// Check status +// Check status, if error pass along -errno as the result. // if (!Status.IsOK()) - {rhP->Sched(XrdPosixMap::Result(Status)); + {rhP->Sched(XrdPosixMap::Result(Status,ecMsg,false)); unRef(); } } @@ -584,7 +585,7 @@ int XrdPosixFile::Read (char *Buff, long long Offs, int Len) Status = clFile.Read((uint64_t)Offs, (uint32_t)Len, Buff, bytes); unRef(); - return (Status.IsOK() ? (int)bytes : XrdPosixMap::Result(Status, false)); + return (Status.IsOK() ? (int)bytes : XrdPosixMap::Result(Status,ecMsg,false)); } /******************************************************************************/ @@ -608,10 +609,10 @@ void XrdPosixFile::Read (XrdOucCacheIOCB &iocb, char *buff, long long offs, if (doPgRd) Status = clFile.PgRead((uint64_t)offs,(uint32_t)rlen,buff,rhP); else Status = clFile.Read ((uint64_t)offs,(uint32_t)rlen,buff,rhP); -// Check status +// Check status. Upon error pass along -errno as the result. // if (!Status.IsOK()) - {rhP->Sched(XrdPosixMap::Result(Status, false)); + {rhP->Sched(XrdPosixMap::Result(Status, ecMsg, false)); unRef(); } } @@ -646,9 +647,9 @@ int XrdPosixFile::ReadV (const XrdOucIOVec *readV, int n) unRef(); delete vrInfo; -// Return appropriate result +// Return appropriate result (here we return -errno as the result) // - return (Status.IsOK() ? nbytes : XrdPosixMap::Result(Status, false)); + return (Status.IsOK() ? nbytes : XrdPosixMap::Result(Status, ecMsg, false)); } /******************************************************************************/ @@ -680,7 +681,7 @@ void XrdPosixFile::ReadV(XrdOucCacheIOCB &iocb, const XrdOucIOVec *readV, int n) // Return appropriate result // if (!Status.IsOK()) - {rhp->Sched(XrdPosixMap::Result(Status, false)); + {rhp->Sched(XrdPosixMap::Result(Status, ecMsg, false)); unRef(); } } @@ -744,7 +745,7 @@ int XrdPosixFile::Sync() // Return result // - return XrdPosixMap::Result(Status, false); + return XrdPosixMap::Result(Status, ecMsg, false); } /******************************************************************************/ @@ -761,7 +762,7 @@ void XrdPosixFile::Sync(XrdOucCacheIOCB &iocb) // Check status // - if (!Status.IsOK()) rhp->Sched(XrdPosixMap::Result(Status, false)); + if (!Status.IsOK()) rhp->Sched(XrdPosixMap::Result(Status, ecMsg, false)); } /******************************************************************************/ @@ -780,7 +781,7 @@ int XrdPosixFile::Trunc(long long Offset) // Return results // - return XrdPosixMap::Result(Status); + return XrdPosixMap::Result(Status,ecMsg,false); } /******************************************************************************/ @@ -797,7 +798,7 @@ int XrdPosixFile::Write(char *Buff, long long Offs, int Len) Status = clFile.Write((uint64_t)Offs, (uint32_t)Len, Buff); unRef(); - return (Status.IsOK() ? Len : XrdPosixMap::Result(Status)); + return (Status.IsOK() ? Len : XrdPosixMap::Result(Status,ecMsg,false)); } /******************************************************************************/ @@ -817,7 +818,7 @@ void XrdPosixFile::Write(XrdOucCacheIOCB &iocb, char *buff, long long offs, // Check status // if (!Status.IsOK()) - {rhp->Sched(XrdPosixMap::Result(Status)); + {rhp->Sched(XrdPosixMap::Result(Status,ecMsg,false)); unRef(); } } diff --git a/src/XrdPosix/XrdPosixFileRH.cc b/src/XrdPosix/XrdPosixFileRH.cc index 47ab3c0a298..a892f807299 100644 --- a/src/XrdPosix/XrdPosixFileRH.cc +++ b/src/XrdPosix/XrdPosixFileRH.cc @@ -111,7 +111,8 @@ void XrdPosixFileRH::HandleResponse(XrdCl::XRootDStatus *status, // Determine ending status. Note: error indicated as result set to -errno. // - if (!(status->IsOK())) result = XrdPosixMap::Result(*status, false); + if (!(status->IsOK())) + result = XrdPosixMap::Result(*status, theFile->ecMsg, false); else if (typeIO == nonIO) result = 0; else if (typeIO == isRead) {XrdCl::ChunkInfo *cInfo = 0; diff --git a/src/XrdPosix/XrdPosixMap.cc b/src/XrdPosix/XrdPosixMap.cc index 7796c926e50..ec8caed292e 100644 --- a/src/XrdPosix/XrdPosixMap.cc +++ b/src/XrdPosix/XrdPosixMap.cc @@ -31,6 +31,7 @@ #include #include +#include "XrdOuc/XrdOucECMsg.hh" #include "XProtocol/XProtocol.hh" #include "XrdPosix/XrdPosixMap.hh" #include "XrdSfs/XrdSfsFlags.hh" @@ -146,9 +147,9 @@ XrdCl::Access::Mode XrdPosixMap::Mode2Access(mode_t mode) /* R e s u l t */ /******************************************************************************/ -int XrdPosixMap::Result(const XrdCl::XRootDStatus &Status, bool retneg1) +int XrdPosixMap::Result(const XrdCl::XRootDStatus &Status, + XrdOucECMsg& ecMsg, bool retneg1) { - std::string eText; int eNum; // If all went well, return success @@ -158,21 +159,21 @@ int XrdPosixMap::Result(const XrdCl::XRootDStatus &Status, bool retneg1) // If this is an xrootd error then get the xrootd generated error // if (Status.code == XrdCl::errErrorResponse) - {eText = Status.GetErrorMessage(); + {ecMsg = Status.GetErrorMessage(); eNum = XProtocol::toErrno(Status.errNo); } else { - eText = Status.ToStr(); + ecMsg = Status.ToStr(); eNum = (Status.errNo ? Status.errNo : mapCode(Status.code)); } // Trace this if need be (we supress this for as we really need more info to // make this messae useful like the opteration and path). // -// if (eNum != ENOENT && !eText.empty() && Debug) +// if (eNum != ENOENT && !ecMsg.hasMsg() && Debug) // std::cerr <<"XrdPosix: " < #include +class XrdOucECMsg; + class XrdPosixMap { public: @@ -46,7 +48,7 @@ static mode_t Flags2Mode(dev_t *rdv, uint32_t flags); static XrdCl::Access::Mode Mode2Access(mode_t mode); static int Result(const XrdCl::XRootDStatus &Status, - bool retneg1=false); + XrdOucECMsg& ecMsg, bool retneg1=false); static void SetDebug(bool dbg) {Debug = dbg;} diff --git a/src/XrdPosix/XrdPosixObject.cc b/src/XrdPosix/XrdPosixObject.cc index 2c986c333e5..3e054d0e64b 100644 --- a/src/XrdPosix/XrdPosixObject.cc +++ b/src/XrdPosix/XrdPosixObject.cc @@ -38,6 +38,15 @@ #include "XrdSys/XrdSysHeaders.hh" #include "XrdSys/XrdSysTimer.hh" +/******************************************************************************/ +/* G l o b a l s */ +/******************************************************************************/ + +namespace XrdPosixGlobals +{ +extern thread_local XrdOucECMsg ecMsg; +} + /******************************************************************************/ /* S t a t i c M e m b e r s */ /******************************************************************************/ diff --git a/src/XrdPosix/XrdPosixObject.hh b/src/XrdPosix/XrdPosixObject.hh index 0b7b5ebc7b1..578a0d6ed55 100644 --- a/src/XrdPosix/XrdPosixObject.hh +++ b/src/XrdPosix/XrdPosixObject.hh @@ -32,6 +32,7 @@ #include +#include "XrdOuc/XrdOucECMsg.hh" #include "XrdSys/XrdSysAtomics.hh" #include "XrdSys/XrdSysPthread.hh" @@ -52,6 +53,8 @@ static XrdPosixFile *File(int fildes, bool glk=false); int FDNum() {return fdNum;} + XrdOucECMsg* getECMsg() {return &ecMsg;} + static int Init(int numfd); void Lock(bool wr=true) @@ -87,9 +90,11 @@ virtual bool Who(XrdPosixDir **dirP) {return false;} virtual bool Who(XrdPosixFile **fileP) {return false;} - XrdPosixObject() : fdNum(-1), refCnt(0) {} + XrdPosixObject() : ecMsg("[posix]"),fdNum(-1),refCnt(0) {} virtual ~XrdPosixObject() {if (fdNum >= 0) Release(this);} + XrdOucECMsg ecMsg; + protected: XrdSysRecMutex updMutex; XrdSysRWLock objMutex; diff --git a/src/XrdPosix/XrdPosixPrepIO.cc b/src/XrdPosix/XrdPosixPrepIO.cc index 27846075b57..47c80ed4b25 100644 --- a/src/XrdPosix/XrdPosixPrepIO.cc +++ b/src/XrdPosix/XrdPosixPrepIO.cc @@ -99,7 +99,7 @@ bool XrdPosixPrepIO::Init(XrdOucCacheIOCB *iocbP) // Make sure all went well. If so, do a Stat() call on the underlying file // if (Status.IsOK()) fileP->Stat(Status); - else {openRC = XrdPosixMap::Result(Status, false); + else {openRC = XrdPosixMap::Result(Status, fileP->ecMsg, false); if (DEBUGON && errno != ENOENT && errno != ELOOP) {std::string eTxt = Status.ToString(); DEBUG(eTxt<<" deferred open "<Origin()); diff --git a/src/XrdPosix/XrdPosixXrootd.cc b/src/XrdPosix/XrdPosixXrootd.cc index 64494207bc6..bfd28557889 100644 --- a/src/XrdPosix/XrdPosixXrootd.cc +++ b/src/XrdPosix/XrdPosixXrootd.cc @@ -51,6 +51,7 @@ #include "XrdSys/XrdSysPlatform.hh" #include "XrdOuc/XrdOucCache.hh" +#include "XrdOuc/XrdOucECMsg.hh" #include "XrdOuc/XrdOucEnv.hh" #include "XrdOuc/XrdOucName2Name.hh" #include "XrdOuc/XrdOucPsx.hh" @@ -79,6 +80,8 @@ class XrdSysError; namespace XrdPosixGlobals { +thread_local XrdOucECMsg ecMsg("[posix]"); + XrdScheduler *schedP = 0; XrdOucCache *theCache = 0; XrdOucName2Name *theN2N = 0; @@ -147,10 +150,7 @@ int OpenDefer(XrdPosixFile *fp, // Assign a file descriptor to this file // if (!(fp->AssignFD(isStream))) - {delete fp; - errno = EMFILE; - return -1; - } + {delete fp; return XrdPosixGlobals::ecMsg.SetErrno(EMFILE);} // Allocate a prepare I/O object to defer this open // @@ -240,7 +240,7 @@ XrdPosixXrootd::~XrdPosixXrootd() int XrdPosixXrootd::Access(const char *path, int amode) { - XrdPosixAdmin admin(path); + XrdPosixAdmin admin(path,XrdPosixGlobals::ecMsg); mode_t stMode; bool aOK = true; @@ -257,8 +257,7 @@ int XrdPosixXrootd::Access(const char *path, int amode) // All done // if (aOK) return 0; - errno = EACCES; - return -1; + return XrdPosixGlobals::ecMsg.SetErrno(EACCES); } /******************************************************************************/ @@ -276,7 +275,7 @@ int XrdPosixXrootd::Close(int fildes) // number so no one can reference this file again. // if (!(fP = XrdPosixObject::ReleaseFile(fildes))) - {errno = EBADF; return -1;} + return XrdPosixGlobals::ecMsg.SetErrno(EBADF); // Detach the file from a possible cache. We need to up the reference count // to synchrnoize with any possible callback as we may need to place this @@ -299,9 +298,9 @@ int XrdPosixXrootd::Close(int fildes) // if (fP) XrdPosixFile::DelayedDestroy(fP); -// Return final result +// Return final result. Note: close errors are recorded in global thread status // - return (ret ? 0 : XrdPosixMap::Result(Status)); + return (ret ? 0 : XrdPosixMap::Result(Status,XrdPosixGlobals::ecMsg,true)); } /******************************************************************************/ @@ -316,7 +315,7 @@ int XrdPosixXrootd::Closedir(DIR *dirp) // Get the directory object // if (!(dP = XrdPosixObject::ReleaseDir(fildes))) - {errno = EBADF; return -1;} + return XrdPosixGlobals::ecMsg.SetErrno(EBADF); // Deallocate the directory // @@ -385,7 +384,7 @@ int XrdPosixXrootd::Fstat(int fildes, struct stat *buf) if (rc <= 0) {fp->UnLock(); if (!rc) return 0; - errno = -rc; + errno = -rc; //??? return -1; } @@ -464,7 +463,7 @@ int XrdPosixXrootd::Ftruncate(int fildes, off_t offset) long long XrdPosixXrootd::Getxattr (const char *path, const char *name, void *value, unsigned long long size) { - XrdPosixAdmin admin(path); + XrdPosixAdmin admin(path,XrdPosixGlobals::ecMsg); XrdCl::QueryCode::Code reqCode; int vsize = static_cast(size); @@ -478,7 +477,7 @@ long long XrdPosixXrootd::Getxattr (const char *path, const char *name, { if (!strcmp(name,"xroot.cksum")) reqCode=XrdCl::QueryCode::Checksum; else if (!strcmp(name,"xroot.space")) reqCode=XrdCl::QueryCode::Space; else if (!strcmp(name,"xroot.xattr")) reqCode=XrdCl::QueryCode::XAttr; - else {errno = ENOATTR; return -1;} + else {errno = ENOATTR; return -1;} //??? }else {errno = EINVAL; return -1;} // Stat the file first to allow vectoring of the request to the right server @@ -527,7 +526,7 @@ off_t XrdPosixXrootd::Lseek(int fildes, off_t offset, int whence) int XrdPosixXrootd::Mkdir(const char *path, mode_t mode) { - XrdPosixAdmin admin(path); + XrdPosixAdmin admin(path,XrdPosixGlobals::ecMsg); XrdCl::MkDirFlags::Flags flags; // Preferentially make the whole path unless told otherwise @@ -543,7 +542,8 @@ int XrdPosixXrootd::Mkdir(const char *path, mode_t mode) // return XrdPosixMap::Result(admin.Xrd.MkDir(admin.Url.GetPathWithParams(), flags, - XrdPosixMap::Mode2Access(mode)) + XrdPosixMap::Mode2Access(mode)), + XrdPosixGlobals::ecMsg,true ); } @@ -583,7 +583,7 @@ int XrdPosixXrootd::Open(const char *path, int oflags, mode_t mode, // if (oflags & isStream) {if (XrdPosixObject::CanStream()) Opts |= XrdPosixFile::isStrm; - else {errno = EMFILE; return -1;} + return XrdPosixGlobals::ecMsg.SetErrno(EMFILE); } // Translate create vs simple open. Always make dirpath on create! @@ -600,9 +600,7 @@ int XrdPosixXrootd::Open(const char *path, int oflags, mode_t mode, // Allocate the new file object // if (!(fp = new XrdPosixFile(aOK, path, cbP, Opts))) - {errno = EMFILE; - return -1; - } + return XrdPosixGlobals::ecMsg.SetErrno(EMFILE); // Check if all went well during allocation // @@ -620,7 +618,7 @@ int XrdPosixXrootd::Open(const char *path, int oflags, mode_t mode, } rc = XrdPosixGlobals::theCache->Prepare(fp->Path(), oflags, mode); if (rc > 0) return OpenDefer(fp, cbP, XOflags, XOmode, oflags&isStream); - if (rc < 0) {delete fp; errno = -rc; return -1;} + if (rc < 0) {delete fp; return XrdPosixGlobals::ecMsg.SetErrno(-rc);} } // Open the file (sync or async) @@ -634,14 +632,11 @@ int XrdPosixXrootd::Open(const char *path, int oflags, mode_t mode, // if (!Status.IsOK()) {XrdPosixGlobals::Stats.Count(XrdPosixGlobals::Stats.X.OpenErrs); - XrdPosixMap::Result(Status); - int rc = errno; - if (DEBUGON && rc != ENOENT && rc != ELOOP) - {std::string eTxt = Status.ToString(); - DEBUG(eTxt <<" open " <Origin()); - } + int rc = XrdPosixMap::Result(Status,XrdPosixGlobals::ecMsg,false); + if (DEBUGON && rc != -ENOENT && rc != -ELOOP) + {DEBUG(XrdPosixGlobals::ecMsg.Msg() <<" open " <Origin());} delete fp; - errno = rc; + errno = -rc; // Saved errno across the delete return -1; } @@ -649,8 +644,7 @@ int XrdPosixXrootd::Open(const char *path, int oflags, mode_t mode, // if (!(fp->AssignFD(oflags & isStream))) {delete fp; - errno = EMFILE; - return -1; + return XrdPosixGlobals::ecMsg.SetErrno(EMFILE); } // Finalize the open (this gets the stat info). For async opens, the @@ -658,7 +652,7 @@ int XrdPosixXrootd::Open(const char *path, int oflags, mode_t mode, // if (cbP) {errno = EINPROGRESS; return -1;} if (fp->Finalize(&Status)) return fp->FDNum(); - return XrdPosixMap::Result(Status); + return XrdPosixMap::Result(Status,XrdPosixGlobals::ecMsg,true); } /******************************************************************************/ @@ -701,14 +695,16 @@ DIR* XrdPosixXrootd::Opendir(const char *path) // Get a new directory object // if (!(dP = new XrdPosixDir(path))) - {errno = ENOMEM; return (DIR *)0;} + {XrdPosixGlobals::ecMsg.SetErrno(ENOMEM); + return (DIR*)0; + } // Assign a file descriptor to this file // if (!(dP->AssignFD())) {delete dP; - errno = EMFILE; - return (DIR *)0; + XrdPosixGlobals::ecMsg.SetErrno(EMFILE); + return (DIR*)0; } // Open the directory @@ -719,7 +715,7 @@ DIR* XrdPosixXrootd::Opendir(const char *path) // rc = errno; delete dP; - errno = rc; + errno = rc; // Restore saved errno return (DIR *)0; } @@ -735,18 +731,20 @@ ssize_t XrdPosixXrootd::Pread(int fildes, void *buf, size_t nbyte, off_t offset) // Find the file object // - if (!(fp = XrdPosixObject::File(fildes))) return -1; + if (!(fp = XrdPosixObject::File(fildes))) + return XrdPosixGlobals::ecMsg.SetErrno(EBADF); // Make sure the size is not too large // - if (nbyte > (size_t)0x7fffffff) return Fault(fp,EOVERFLOW); + if (nbyte > (size_t)0x7fffffff) + return Fault(fp, EOVERFLOW, "read size too large"); else iosz = static_cast(nbyte); // Issue the read // offs = static_cast(offset); bytes = fp->XCio->Read((char *)buf, offs, (int)iosz); - if (bytes < 0) return Fault(fp,-bytes); + if (bytes < 0) return Fault(fp,-bytes,"*"); // All went well // @@ -771,7 +769,7 @@ void XrdPosixXrootd::Pread(int fildes, void *buf, size_t nbyte, off_t offset, // if (nbyte > (size_t)0x7fffffff) {fp->UnLock(); - errno = EOVERFLOW; + fp->ecMsg.SetErrno(EOVERFLOW,0,"read size too large"); cbp->Complete(-1); return; } @@ -804,14 +802,15 @@ ssize_t XrdPosixXrootd::Pwrite(int fildes, const void *buf, size_t nbyte, off_t // Make sure the size is not too large // - if (nbyte > (size_t)0x7fffffff) return Fault(fp,EOVERFLOW); + if (nbyte > (size_t)0x7fffffff) + return Fault(fp,EOVERFLOW,"write size too large"); else iosz = static_cast(nbyte); // Issue the write // offs = static_cast(offset); bytes = fp->XCio->Write((char *)buf, offs, (int)iosz); - if (bytes < 0) return Fault(fp,-bytes); + if (bytes < 0) return Fault(fp,-bytes,"*"); // All went well // @@ -837,7 +836,7 @@ void XrdPosixXrootd::Pwrite(int fildes, const void *buf, size_t nbyte, // if (nbyte > (size_t)0x7fffffff) {fp->UnLock(); - errno = EOVERFLOW; + fp->ecMsg.SetErrno(EOVERFLOW,0,"read size too large"); cbp->Complete(-1); return; } @@ -982,7 +981,9 @@ struct dirent64* XrdPosixXrootd::Readdir64(DIR *dirp) // Find the object // if (!(dP = XrdPosixObject::Dir(fildes))) - {errno = EBADF; return 0;} + {XrdPosixGlobals::ecMsg.SetErrno(EBADF); + return (dirent64*)0; + } // Get the next directory entry // @@ -1049,12 +1050,13 @@ int XrdPosixXrootd::Readdir64_r(DIR *dirp, struct dirent64 *entry, int XrdPosixXrootd::Rename(const char *oldpath, const char *newpath) { - XrdPosixAdmin admin(oldpath); + XrdPosixAdmin admin(oldpath,XrdPosixGlobals::ecMsg); XrdCl::URL newUrl((std::string)newpath); // Make sure the admin is OK and the new url is valid // - if (!admin.isOK() || !newUrl.IsValid()) {errno = EINVAL; return -1;} + if (!admin.isOK() || !newUrl.IsValid()) + return XrdPosixGlobals::ecMsg.SetErrno(EINVAL); // Issue rename to he cache (it really should just deep-six both files) // @@ -1068,10 +1070,11 @@ int XrdPosixXrootd::Rename(const char *oldpath, const char *newpath) // Issue the rename // if (XrdPosixGlobals::usingEC) - return EcRename(oldpath, newpath, &admin); + return EcRename(oldpath, newpath, admin); return XrdPosixMap::Result(admin.Xrd.Mv(admin.Url.GetPathWithParams(), - newUrl.GetPathWithParams())); + newUrl.GetPathWithParams()), + XrdPosixGlobals::ecMsg, true); } /******************************************************************************/ @@ -1097,7 +1100,7 @@ void XrdPosixXrootd::Rewinddir(DIR *dirp) int XrdPosixXrootd::Rmdir(const char *path) { - XrdPosixAdmin admin(path); + XrdPosixAdmin admin(path,XrdPosixGlobals::ecMsg); // Make sure the admin is OK // @@ -1113,7 +1116,8 @@ int XrdPosixXrootd::Rmdir(const char *path) // Issue the rmdir // - return XrdPosixMap::Result(admin.Xrd.RmDir(admin.Url.GetPathWithParams())); + return XrdPosixMap::Result(admin.Xrd.RmDir(admin.Url.GetPathWithParams()), + XrdPosixGlobals::ecMsg, true); } /******************************************************************************/ @@ -1145,7 +1149,7 @@ void XrdPosixXrootd::Seekdir(DIR *dirp, long loc) int XrdPosixXrootd::Stat(const char *path, struct stat *buf) { - XrdPosixAdmin admin(path); + XrdPosixAdmin admin(path,XrdPosixGlobals::ecMsg); // Make sure the admin is OK // @@ -1162,13 +1166,13 @@ int XrdPosixXrootd::Stat(const char *path, struct stat *buf) if (!statX.path) return -1; int rc = XrdPosixGlobals::theCache->Stat(statX.path, *buf); if (!rc) return 0; - if (rc < 0) {errno = -rc; return -1;} + if (rc < 0) {errno = -rc; return -1;} // does the cache set this??? } // Issue the stat and verify that all went well // if (XrdPosixGlobals::usingEC) - return EcStat(path, buf, &admin); + return EcStat(path, buf, admin); if (!admin.Stat(*buf)) return -1; return 0; @@ -1220,7 +1224,7 @@ int XrdPosixXrootd::Statvfs(const char *path, struct statvfs *buf) static const int szVFS = sizeof(buf->f_bfree); static const long long max32 = 0x7fffffffLL; - XrdPosixAdmin admin(path); + XrdPosixAdmin admin(path,XrdPosixGlobals::ecMsg); XrdCl::StatInfoVFS *vfsStat; long long rwFree, ssFree, rwBlks; @@ -1233,7 +1237,8 @@ int XrdPosixXrootd::Statvfs(const char *path, struct statvfs *buf) // Issue the statfvs call // if (XrdPosixMap::Result(admin.Xrd.StatVFS(admin.Url.GetPathWithParams(), - vfsStat)) < 0) return -1; + vfsStat), + XrdPosixGlobals::ecMsg) < 0) return -1; // Extract out the information // @@ -1289,7 +1294,7 @@ long XrdPosixXrootd::Telldir(DIR *dirp) // Find the object // if (!(dP = XrdPosixObject::Dir(fildes))) - {errno = EBADF; return 0;} + return XrdPosixGlobals::ecMsg.SetErrno(EBADF,0); // Tell the current directory location // @@ -1304,7 +1309,7 @@ long XrdPosixXrootd::Telldir(DIR *dirp) int XrdPosixXrootd::Truncate(const char *path, off_t Size) { - XrdPosixAdmin admin(path); + XrdPosixAdmin admin(path,XrdPosixGlobals::ecMsg); uint64_t tSize = static_cast(Size); // Make sure the admin is OK @@ -1322,7 +1327,8 @@ int XrdPosixXrootd::Truncate(const char *path, off_t Size) // Issue the truncate to the origin // std::string urlp = admin.Url.GetPathWithParams(); - return XrdPosixMap::Result(admin.Xrd.Truncate(urlp,tSize)); + return XrdPosixMap::Result(admin.Xrd.Truncate(urlp,tSize), + XrdPosixGlobals::ecMsg,true); } /******************************************************************************/ @@ -1331,7 +1337,7 @@ int XrdPosixXrootd::Truncate(const char *path, off_t Size) int XrdPosixXrootd::Unlink(const char *path) { - XrdPosixAdmin admin(path); + XrdPosixAdmin admin(path,XrdPosixGlobals::ecMsg); // Make sure the admin is OK // @@ -1348,9 +1354,10 @@ int XrdPosixXrootd::Unlink(const char *path) // Issue the UnLink // if (XrdPosixGlobals::usingEC) - return EcUnlink(path, &admin); + return EcUnlink(path, admin); - return XrdPosixMap::Result(admin.Xrd.Rm(admin.Url.GetPathWithParams())); + return XrdPosixMap::Result(admin.Xrd.Rm(admin.Url.GetPathWithParams()), + XrdPosixGlobals::ecMsg, true); } /******************************************************************************/ @@ -1439,7 +1446,7 @@ bool XrdPosixXrootd::myFD(int fd) int XrdPosixXrootd::QueryChksum(const char *path, time_t &Mtime, char *value, int vsize) { - XrdPosixAdmin admin(path); + XrdPosixAdmin admin(path,XrdPosixGlobals::ecMsg); // Stat the file first to allow vectoring of the request to the right server // @@ -1450,13 +1457,51 @@ int XrdPosixXrootd::QueryChksum(const char *path, time_t &Mtime, return admin.Query(XrdCl::QueryCode::Checksum, value, vsize); } +/******************************************************************************/ +/* Q u e r y E r r o r */ +/******************************************************************************/ + +int XrdPosixXrootd::QueryError(std::string& emsg, int fd, bool reset) +{ + XrdOucECMsg* ecmP; + +// If global wanted then use that one otherwise find the object specific one +// + if (fd < 0) ecmP = &XrdPosixGlobals::ecMsg; + else {XrdPosixFile *fp; + if (!(fp = XrdPosixObject::File(fd))) return -1; + ecmP = fp->getECMsg(); + } + +// Return the message information +// + return ecmP->Get(emsg, reset); +} + +/******************************************************************************/ + +int XrdPosixXrootd::QueryError(std::string& emsg, DIR* dirP, bool reset) +{ + XrdPosixDir *dP; + int fildes = XrdPosixDir::dirNo(dirP); + +// Find the object +// + if (!(dP = XrdPosixObject::Dir(fildes))) + return XrdPosixGlobals::ecMsg.SetErrno(EBADF); + +// Return result +// + return dP->getECMsg()->Get(emsg, reset); +} + /******************************************************************************/ /* Q u e r y O p a q u e */ /******************************************************************************/ long long XrdPosixXrootd::QueryOpaque(const char *path, char *value, int size) { - XrdPosixAdmin admin(path); + XrdPosixAdmin admin(path,XrdPosixGlobals::ecMsg); // Stat the file first to allow vectoring of the request to the right server // @@ -1474,17 +1519,18 @@ long long XrdPosixXrootd::QueryOpaque(const char *path, char *value, int size) /* F a u l t */ /******************************************************************************/ -int XrdPosixXrootd::Fault(XrdPosixFile *fp, int ecode) +int XrdPosixXrootd::Fault(XrdPosixFile *fp, int ecode, const char *msg) { fp->UnLock(); - errno = ecode; - return -1; + return XrdPosixGlobals::ecMsg.SetErrno(ecode, -1, msg); } /******************************************************************************/ /* E c R e n a m e */ /******************************************************************************/ -int XrdPosixXrootd::EcRename(const char *oldpath, const char *newpath, XrdPosixAdmin *admin) + +int XrdPosixXrootd::EcRename(const char *oldpath, const char *newpath, + XrdPosixAdmin &admin) { XrdCl::URL url(oldpath); XrdCl::URL newUrl(newpath); @@ -1503,8 +1549,9 @@ int XrdPosixXrootd::EcRename(const char *oldpath, const char *newpath, XrdPosixA || queryResp->ToString() == "server\n") { if (queryResp) delete queryResp; - return XrdPosixMap::Result(admin->Xrd.Mv(admin->Url.GetPathWithParams(), - newUrl.GetPathWithParams())); + return XrdPosixMap::Result(admin.Xrd.Mv(admin.Url.GetPathWithParams(), + newUrl.GetPathWithParams()), + XrdPosixGlobals::ecMsg, true); } else if (queryResp) delete queryResp; @@ -1512,32 +1559,27 @@ int XrdPosixXrootd::EcRename(const char *oldpath, const char *newpath, XrdPosixA st = fs.DeepLocate("*", XrdCl::OpenFlags::None, info ); std::unique_ptr ptr( info ); if( !st.IsOK() ) - { - errno = ENOENT; - return -1; - } + return XrdPosixMap::Result(st, XrdPosixGlobals::ecMsg, true); // check if this is a file or a dir, do not support dir renaming in EC struct stat buf; - if (XrdPosixXrootd::Stat(oldpath, &buf) != 0 || ! S_ISREG(buf.st_mode)) - { - errno = ENOTSUP; - return -1; - } - if (XrdPosixXrootd::Stat(newpath, &buf) == 0) - { - errno = ENOTSUP; - return -1; - } + if (XrdPosixXrootd::Stat(oldpath, &buf) != 0) + return XrdPosixGlobals::ecMsg.SetErrno(ENOENT); + if ( ! S_ISREG(buf.st_mode)) + return XrdPosixGlobals::ecMsg.SetErrno(ENOTSUP); + + if (XrdPosixXrootd::Stat(newpath, &buf) == 0) + return XrdPosixGlobals::ecMsg.SetErrno(EEXIST); int rc = -ENOENT; for( size_t i = 0; i < info->GetSize(); ++i ) { std::string url_i = "root://" + info->At(i).GetAddress() + "/" + file; - XrdPosixAdmin *admin_i = new XrdPosixAdmin(url_i.c_str()); + XrdPosixAdmin *admin_i = new XrdPosixAdmin(url_i.c_str(),admin.ecMsg); int x = XrdPosixMap::Result(admin_i->Xrd.Mv(admin_i->Url.GetPathWithParams(), - newUrl.GetPathWithParams())); + newUrl.GetPathWithParams()), + admin.ecMsg); if (x != -ENOENT && rc != 0) rc = x; if (admin_i) delete admin_i; @@ -1548,7 +1590,9 @@ int XrdPosixXrootd::EcRename(const char *oldpath, const char *newpath, XrdPosixA /******************************************************************************/ /* E c S t a t */ /******************************************************************************/ -int XrdPosixXrootd::EcStat(const char *path, struct stat *buf, XrdPosixAdmin *admin) + +int XrdPosixXrootd::EcStat(const char *path, struct stat *buf, + XrdPosixAdmin &admin) { XrdCl::URL url(path); std::string file = url.GetPath(); @@ -1569,7 +1613,7 @@ int XrdPosixXrootd::EcStat(const char *path, struct stat *buf, XrdPosixAdmin *ad || queryResp->ToString() == "server\n") { if (queryResp) delete queryResp; - if (!admin->Stat(*buf)) + if (!admin.Stat(*buf)) return -1; else { @@ -1603,7 +1647,7 @@ int XrdPosixXrootd::EcStat(const char *path, struct stat *buf, XrdPosixAdmin *ad for( size_t i = 0; i < info->GetSize(); ++i ) { std::string url_i = "root://" + info->At(i).GetAddress() + "/" + file; - XrdPosixAdmin *admin_i = new XrdPosixAdmin(url_i.c_str()); + XrdPosixAdmin *admin_i = new XrdPosixAdmin(url_i.c_str(),admin.ecMsg); if (admin_i->Stat(buf_i)) { @@ -1652,7 +1696,8 @@ int XrdPosixXrootd::EcStat(const char *path, struct stat *buf, XrdPosixAdmin *ad /******************************************************************************/ /* E c U n l i n k */ /******************************************************************************/ -int XrdPosixXrootd::EcUnlink(const char *path, XrdPosixAdmin *admin) + +int XrdPosixXrootd::EcUnlink(const char *path, XrdPosixAdmin &admin) { XrdCl::URL url(path); std::string file = url.GetPath(); @@ -1668,7 +1713,8 @@ int XrdPosixXrootd::EcUnlink(const char *path, XrdPosixAdmin *admin) || queryResp->ToString() == "server\n") { if (queryResp) delete queryResp; - return XrdPosixMap::Result(admin->Xrd.Rm(admin->Url.GetPathWithParams())); + return XrdPosixMap::Result(admin.Xrd.Rm(admin.Url.GetPathWithParams()), + admin.ecMsg, true); } else if (queryResp) delete queryResp; @@ -1676,16 +1722,16 @@ int XrdPosixXrootd::EcUnlink(const char *path, XrdPosixAdmin *admin) st = fs.DeepLocate("*", XrdCl::OpenFlags::None, info ); std::unique_ptr ptr( info ); if( !st.IsOK() ) - { - errno = ENOENT; - return -1; - } + return XrdPosixMap::Result(st, admin.ecMsg, true); + int rc = -ENOENT; for( size_t i = 0; i < info->GetSize(); ++i ) { std::string url_i = "root://" + info->At(i).GetAddress() + "/" + file; - XrdPosixAdmin *admin_i = new XrdPosixAdmin(url_i.c_str()); - int x = XrdPosixMap::Result(admin_i->Xrd.Rm(admin_i->Url.GetPathWithParams())); + XrdPosixAdmin *admin_i = new XrdPosixAdmin(url_i.c_str(),admin.ecMsg); + int x = XrdPosixMap::Result(admin_i-> + Xrd.Rm(admin_i->Url.GetPathWithParams()), + admin.ecMsg); if (x != -ENOENT && rc != 0) rc = x; if (admin_i) delete admin_i; diff --git a/src/XrdPosix/XrdPosixXrootd.hh b/src/XrdPosix/XrdPosixXrootd.hh index 2902af5199e..bcf9e0123fe 100644 --- a/src/XrdPosix/XrdPosixXrootd.hh +++ b/src/XrdPosix/XrdPosixXrootd.hh @@ -31,6 +31,7 @@ /* Modified by Frank Winklmeier to add the full Posix file system definition. */ /******************************************************************************/ +#include #include #include #include @@ -207,6 +208,27 @@ static void Pwrite(int fildes, const void *buf, size_t nbyte, off_t offset, static int QueryChksum(const char *path, time_t &mtime, char *buff, int blen); +//----------------------------------------------------------------------------- +//! QueryError() is a POSIX extension and returns extended information about +//! the last error returned from a call to a POSIX function. +//! +//! @param emsg Reference to a string to hold the retruned message text. +//! @param fd The file descriptor associated with the error. A negative +//! value returns the last error encountered on the calling +//! thread for the last function not releated to a file descritor. +//! dirP Get the error associated with the last directory operation. +//! @param reset When true (the default) clears the error information. +//! +//! @return The error code and the associated message via parameter emsg. +//! A zero return indicates that no error information is available. +//! A -1 return indicates the call was bad itself because either the +//! fd or dirP was invalid. +//----------------------------------------------------------------------------- + +static int QueryError(std::string& emsg, int fd=-1, bool reset=true); + +static int QueryError(std::string& emsg, DIR* dirP, bool reset=true); + //----------------------------------------------------------------------------- //! QueryOpaque() is a POSIX extension and returns a file's implementation //! specific information. @@ -368,16 +390,16 @@ static bool myFD(int fd); private: -static int Fault(XrdPosixFile *fp, int ecode); +static int Fault(XrdPosixFile *fp, int ecode, const char *msg=0); static int Open(const char *path, int oflag, mode_t mode, XrdPosixCallBack *cbP, XrdPosixInfo *infoP); static bool OpenCache(XrdPosixFile &file, XrdPosixInfo &Info); // functions that will be used when XrdEC is invoked -static int EcRename(const char*, const char*, XrdPosixAdmin*); -static int EcStat(const char*, struct stat*, XrdPosixAdmin*); -static int EcUnlink(const char*, XrdPosixAdmin*); +static int EcRename(const char*, const char*, XrdPosixAdmin&); +static int EcStat(const char*, struct stat*, XrdPosixAdmin&); +static int EcUnlink(const char*, XrdPosixAdmin&); static int baseFD; static int initDone; diff --git a/src/XrdPss/XrdPss.cc b/src/XrdPss/XrdPss.cc index 0891d5ba53a..ee61badaac4 100644 --- a/src/XrdPss/XrdPss.cc +++ b/src/XrdPss/XrdPss.cc @@ -95,8 +95,10 @@ class XrdScheduler; namespace XrdProxy { +thread_local XrdOucECMsg ecMsg("[pss]"); + static XrdPssSys XrdProxySS; - + XrdSysError eDest(0, "pss_"); XrdScheduler *schedP = 0; @@ -365,7 +367,7 @@ int XrdPssSys::Mkdir(const char *path, mode_t mode, int mkpath, XrdOucEnv *eP) // Simply return the proxied result here // - return (XrdPosixXrootd::Mkdir(pbuff, mode) ? -errno : XrdOssOK); + return (XrdPosixXrootd::Mkdir(pbuff, mode) ? Info(errno) : XrdOssOK); } /******************************************************************************/ @@ -1266,6 +1268,16 @@ int XrdPssFile::Ftruncate(unsigned long long flen) return (XrdPosixXrootd::Ftruncate(fd, flen) ? -errno : XrdOssOK); } +/******************************************************************************/ +/* I n t e r n a l M e t h o d s */ +/******************************************************************************/ + +int XrdPssSys::Info(int rc) +{ + XrdPosixXrootd::QueryError(XrdProxy::ecMsg.Msg()); + return -rc; +} + /******************************************************************************/ /* P 2 D S T */ /******************************************************************************/ diff --git a/src/XrdPss/XrdPss.hh b/src/XrdPss/XrdPss.hh index 631013ea6f5..96179574ded 100644 --- a/src/XrdPss/XrdPss.hh +++ b/src/XrdPss/XrdPss.hh @@ -35,6 +35,7 @@ #include #include #include "XrdSys/XrdSysHeaders.hh" +#include "XrdOuc/XrdOucECMsg.hh" #include "XrdOuc/XrdOucExport.hh" #include "XrdOuc/XrdOucName2Name.hh" #include "XrdOuc/XrdOucPList.hh" @@ -176,6 +177,7 @@ int Unlink(const char *, int Opts=0, XrdOucEnv *eP=0) override; static const int PolNum = 2; enum PolAct {PolPath = 0, PolObj = 1}; +static int Info(int rc); static int P2DST(int &retc, char *hBuff, int hBlen, PolAct pType, const char *path); static int P2OUT(char *pbuff, int pblen, XrdPssUrlInfo &uInfo); diff --git a/src/XrdUtils.cmake b/src/XrdUtils.cmake index 8621c3b974f..42544269cec 100644 --- a/src/XrdUtils.cmake +++ b/src/XrdUtils.cmake @@ -105,6 +105,7 @@ set ( XrdOucSources XrdOuc/XrdOucChkPnt.hh XrdOuc/XrdOucCRC.cc XrdOuc/XrdOucCRC.hh XrdOuc/XrdOucCRC32C.cc XrdOuc/XrdOucCRC32C.hh + XrdOuc/XrdOucECMsg.cc XrdOuc/XrdOucECMsg.hh XrdOuc/XrdOucEnv.cc XrdOuc/XrdOucEnv.hh XrdOuc/XrdOucERoute.cc XrdOuc/XrdOucERoute.hh XrdOuc/XrdOucErrInfo.hh From 8f76a42750ef155d0b5f1bd6f3d056ea474c87e5 Mon Sep 17 00:00:00 2001 From: Andrew Hanushevsky Date: Mon, 13 Nov 2023 08:51:54 -0800 Subject: [PATCH 009/276] [POSIX] Avoid clang variadic argument complaint --- src/XrdPosix/XrdPosixAdmin.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/XrdPosix/XrdPosixAdmin.hh b/src/XrdPosix/XrdPosixAdmin.hh index f56ce54c1bf..9e0d86acee7 100644 --- a/src/XrdPosix/XrdPosixAdmin.hh +++ b/src/XrdPosix/XrdPosixAdmin.hh @@ -55,7 +55,7 @@ XrdOucECMsg& ecMsg; bool isOK() {if (Url.IsValid()) return true; ecMsg.Set(EINVAL, 0); ecMsg.Msgf("PosixAdmin", "url '%s' is invalid", - Url.GetURL()); + Url.GetURL().c_str()); errno = EINVAL; return false; } From 1e2c84c113e17f6819ac48434517e7de5698faa7 Mon Sep 17 00:00:00 2001 From: Diego Davila Date: Tue, 9 Jan 2024 11:31:12 -0800 Subject: [PATCH 010/276] Add option tpc.route to force the Destination IP address on a HTTP-TPC --- src/XrdTpc/XrdTpcConfigure.cc | 17 ++++++++++++++++- src/XrdTpc/XrdTpcTPC.cc | 34 ++++++++++++++++++++++++++++++++++ src/XrdTpc/XrdTpcTPC.hh | 2 ++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/XrdTpc/XrdTpcConfigure.cc b/src/XrdTpc/XrdTpcConfigure.cc index 72c7072b543..55747b1b33f 100644 --- a/src/XrdTpc/XrdTpcConfigure.cc +++ b/src/XrdTpc/XrdTpcConfigure.cc @@ -56,7 +56,22 @@ bool TPCHandler::Configure(const char *configfn, XrdOucEnv *myEnv) Config.Close(); return false; } - } else if (!strcmp("tpc.timeout", val)) { + } else if (!strcmp("tpc.fixed_route", val)) { + if (!(val = Config.GetWord())) { + Config.Close(); + m_log.Emsg("Config", "tpc.fixed_route value not specified"); + return false; + } + if (!strcmp("1", val) || !strcasecmp("yes", val) || !strcasecmp("true", val)) { + m_fixed_route= true; + } else if (!strcmp("0", val) || !strcasecmp("no", val) || !strcasecmp("false", val)) { + m_fixed_route= false; + } else{ + Config.Close(); + m_log.Emsg("Config", "tpc.fixed_route value is invalid", val); + return false; + } + } else if (!strcmp("tpc.timeout", val)) { if (!(val = Config.GetWord())) { m_log.Emsg("Config","tpc.timeout value not specified."); return false; } diff --git a/src/XrdTpc/XrdTpcTPC.cc b/src/XrdTpc/XrdTpcTPC.cc index bd35d8ce79e..49f7dd4477f 100644 --- a/src/XrdTpc/XrdTpcTPC.cc +++ b/src/XrdTpc/XrdTpcTPC.cc @@ -1,5 +1,6 @@ #include "XrdHttp/XrdHttpExtHandler.hh" #include "XrdNet/XrdNetAddr.hh" +#include "XrdNet/XrdNetUtils.hh" #include "XrdOuc/XrdOucEnv.hh" #include "XrdSec/XrdSecEntity.hh" #include "XrdSfs/XrdSfsInterface.hh" @@ -321,6 +322,7 @@ TPCHandler::~TPCHandler() { TPCHandler::TPCHandler(XrdSysError *log, const char *config, XrdOucEnv *myEnv) : m_desthttps(false), + m_fixed_route(false), m_timeout(60), m_first_timeout(120), m_log(log->logger(), "TPC_"), @@ -952,6 +954,38 @@ int TPCHandler::ProcessPullReq(const std::string &resource, XrdHttpExtReq &req) logTransferEvent(LogMask::Error, rec, "PULL_FAIL", msg); return req.SendSimpleResp(rec.status, NULL, NULL, msg, 0); } + // ddavila 2023-01-05: + // The following change was required by the Rucio/SENSE project where + // multiple IP addresses, each from a different subnet, are assigned to a + // single server and routed differently by SENSE. + // The above requires the server to utilize the same IP, that was used to + // start the TPC, for the resolution of the given TPC instead of + // using any of the IPs available. + if (m_fixed_route){ + XrdNetAddr *nP; + int numIP = 0; + char buff[1024]; + char * ip; + + // Get the hostname used to contact the server from the http header + auto host_header = req.headers.find("Host"); + std::string host_used; + if (host_header != req.headers.end()) { + host_used = host_header->second; + } + + // Get the IP addresses associated with the above hostname + XrdNetUtils::GetAddrs(host_used.c_str(), &nP, numIP, XrdNetUtils::prefAuto, 0); + int ip_size = nP[0].Format(buff, 1024, XrdNetAddrInfo::fmtAddr,XrdNetAddrInfo::noPort); + ip = (char *)malloc(ip_size-1); + + // Substring to get only the address, remove brackets and garbage + memcpy(ip, buff+1, ip_size-2); + ip[ip_size-2]='\0'; + logTransferEvent(LogMask::Info, rec, "LOCAL IP", ip); + + curl_easy_setopt(curl, CURLOPT_INTERFACE, ip); + } curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); // curl_easy_setopt(curl,CURLOPT_SOCKOPTFUNCTION,sockopt_setcloexec_callback); curl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, opensocket_callback); diff --git a/src/XrdTpc/XrdTpcTPC.hh b/src/XrdTpc/XrdTpcTPC.hh index 76f600c58c9..d62ce9379fc 100644 --- a/src/XrdTpc/XrdTpcTPC.hh +++ b/src/XrdTpc/XrdTpcTPC.hh @@ -144,6 +144,8 @@ private: static size_t m_block_size; static size_t m_small_block_size; bool m_desthttps; + bool m_fixed_route; // If 'true' the Destination IP in an HTTP-TPC is forced to be the same as the IP used to contact the server + // when 'false' any IP available can be selected int m_timeout; // the 'timeout interval'; if no bytes have been received during this time period, abort the transfer. int m_first_timeout; // the 'first timeout interval'; the amount of time we're willing to wait to get the first byte. // Unless explicitly specified, this is 2x the timeout interval. From d3165b3c73152ac2e37d972b6928125c27f3218e Mon Sep 17 00:00:00 2001 From: Brian Bockelman Date: Thu, 25 Jan 2024 09:46:39 -0600 Subject: [PATCH 011/276] Add support for pelican:// protocol In https://github.com/PelicanPlatform/xrdcl-pelican, we are developing a XrdCl plugin that can talk to the infrastructure for a new project, christening the URL scheme `pelican://`. This commit adds the new schema so it can be utilized from both xrdcp (primarily for testing) and XCache. --- src/XrdApps/XrdCpConfig.cc | 2 ++ src/XrdApps/XrdCpFile.cc | 1 + src/XrdApps/XrdCpFile.hh | 2 +- src/XrdPss/XrdPssUtils.cc | 3 ++- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/XrdApps/XrdCpConfig.cc b/src/XrdApps/XrdCpConfig.cc index 890f80198cc..7f8e8702efa 100644 --- a/src/XrdApps/XrdCpConfig.cc +++ b/src/XrdApps/XrdCpConfig.cc @@ -385,6 +385,7 @@ do{while(optind < Argc && Legacy(optind)) {} if (dstFile->Protocol != XrdCpFile::isFile && dstFile->Protocol != XrdCpFile::isStdIO && dstFile->Protocol != XrdCpFile::isXroot + && dstFile->Protocol != XrdCpFile::isPelican && (!Want(DoAllowHttp) && ((dstFile->Protocol == XrdCpFile::isHttp) || (dstFile->Protocol == XrdCpFile::isHttps)))) {FMSG(dstFile->ProtName <<"file protocol is not supported.", 22)} @@ -903,6 +904,7 @@ void XrdCpConfig::ProcFile(const char *fname) } else if (!((pFile->Protocol == XrdCpFile::isXroot) || (pFile->Protocol == XrdCpFile::isXroots) || + (pFile->Protocol == XrdCpFile::isPelican) || (Want(DoAllowHttp) && ((pFile->Protocol == XrdCpFile::isHttp) || (pFile->Protocol == XrdCpFile::isHttps))))) {FMSG(pFile->ProtName <<" file protocol is not supported.", 22)} diff --git a/src/XrdApps/XrdCpFile.cc b/src/XrdApps/XrdCpFile.cc index a6f8a6496e2..e1e5dc98086 100644 --- a/src/XrdApps/XrdCpFile.cc +++ b/src/XrdApps/XrdCpFile.cc @@ -56,6 +56,7 @@ XrdCpFile::XrdCpFile(const char *FSpec, int &badURL) {"root://", 7, isXroot}, {"roots://", 8, isXroots}, {"http://", 7, isHttp}, + {"pelican://", 10, isPelican}, {"https://", 8, isHttps} }; static int pTnum = sizeof(pTab)/sizeof(struct proto); diff --git a/src/XrdApps/XrdCpFile.hh b/src/XrdApps/XrdCpFile.hh index ef09301b56c..03972c360d8 100644 --- a/src/XrdApps/XrdCpFile.hh +++ b/src/XrdApps/XrdCpFile.hh @@ -38,7 +38,7 @@ class XrdCpFile public: enum PType {isOther = 0, isDir, isFile, isStdIO, - isXroot, isXroots, isHttp, isHttps, isDevNull, isDevZero + isXroot, isXroots, isHttp, isHttps, isPelican, isDevNull, isDevZero }; XrdCpFile *Next; // -> Next file in list diff --git a/src/XrdPss/XrdPssUtils.cc b/src/XrdPss/XrdPssUtils.cc index be14fa55c9a..42f37534f14 100644 --- a/src/XrdPss/XrdPssUtils.cc +++ b/src/XrdPss/XrdPssUtils.cc @@ -42,7 +42,8 @@ namespace struct pEnt {const char *pname; int pnlen;} pTab[] = {{ "https://", 8}, { "http://", 7}, { "roots://", 8}, { "root://", 7}, - {"xroots://", 9}, {"xroot://", 8} + {"xroots://", 9}, {"xroot://", 8}, + {"pelican://", 10} }; int pTNum = sizeof(pTab)/sizeof(pEnt); int xrBeg = 2; From df1471f5a1005b3b33d677c9d04bb4e22760397b Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Thu, 8 Feb 2024 14:25:41 +0100 Subject: [PATCH 012/276] [XrdCeph] Migrate tests to GoogleTest and run with ctest --- src/XrdCeph/src/XrdCeph.cmake | 8 +- src/XrdCeph/tests/CMakeLists.txt | 11 +- src/XrdCeph/tests/XrdCeph.cc | 111 +++++++++++++ src/XrdCeph/tests/XrdCephTests/CMakeLists.txt | 27 ---- .../tests/XrdCephTests/CephParsingTest.cc | 149 ------------------ tests/CMakeLists.txt | 4 - tests/XrdCephTests/CMakeLists.txt | 21 --- tests/XrdCephTests/CephParsingTest.cc | 149 ------------------ 8 files changed, 124 insertions(+), 356 deletions(-) create mode 100644 src/XrdCeph/tests/XrdCeph.cc delete mode 100644 src/XrdCeph/tests/XrdCephTests/CMakeLists.txt delete mode 100644 src/XrdCeph/tests/XrdCephTests/CephParsingTest.cc delete mode 100644 tests/XrdCephTests/CMakeLists.txt delete mode 100644 tests/XrdCephTests/CephParsingTest.cc diff --git a/src/XrdCeph/src/XrdCeph.cmake b/src/XrdCeph/src/XrdCeph.cmake index 60fb1da2099..5277df3fc5b 100644 --- a/src/XrdCeph/src/XrdCeph.cmake +++ b/src/XrdCeph/src/XrdCeph.cmake @@ -1,8 +1,3 @@ -include_directories( ${XROOTD_INCLUDE_DIR} ) -include_directories( ${RADOS_INCLUDE_DIR} ) -include_directories( ${CMAKE_SOURCE_DIR}/src ) - - #------------------------------------------------------------------------------- # XrdCephPosix library version #------------------------------------------------------------------------------- @@ -28,6 +23,9 @@ target_link_libraries( ${XROOTD_LIBRARIES} ${RADOS_LIBS} ) +target_include_directories( + XrdCephPosix PUBLIC ${RADOS_INCLUDE_DIR} $) + set_target_properties( XrdCephPosix PROPERTIES diff --git a/src/XrdCeph/tests/CMakeLists.txt b/src/XrdCeph/tests/CMakeLists.txt index 7216d36fd8e..3b38c7dcd4e 100644 --- a/src/XrdCeph/tests/CMakeLists.txt +++ b/src/XrdCeph/tests/CMakeLists.txt @@ -1 +1,10 @@ -add_subdirectory( XrdCephTests ) +find_package(GTest REQUIRED) + +include(GoogleTest) + +add_executable(xrdceph-unit-tests XrdCeph.cc) + +target_link_libraries(xrdceph-unit-tests + XrdCephPosix GTest::GTest GTest::Main) + +gtest_discover_tests(xrdceph-unit-tests TEST_PREFIX XrdCeph::) diff --git a/src/XrdCeph/tests/XrdCeph.cc b/src/XrdCeph/tests/XrdCeph.cc new file mode 100644 index 00000000000..cfed7cc9cce --- /dev/null +++ b/src/XrdCeph/tests/XrdCeph.cc @@ -0,0 +1,111 @@ +#undef NDEBUG + +#include +#include + +#include + +#define MB 1024 * 1024 + +struct CephFile { + std::string name; + std::string pool; + std::string userId; + unsigned int nbStripes; + unsigned long long stripeUnit; + unsigned long long objectSize; +}; + +void fillCephFile(const char *path, XrdOucEnv *env, CephFile &file); +void fillCephFileParams(const std::string ¶ms, XrdOucEnv *env, + CephFile &file); + +using namespace testing; + +class ParsingTest : public ::testing::Test {}; + +static CephFile parseParam(std::string param, XrdOucEnv *env = NULL) +{ + CephFile cf; + fillCephFileParams(param, env, cf); + return cf; +} + +static CephFile parseFile(std::string param, XrdOucEnv *env = NULL) +{ + CephFile cf; + fillCephFile(param.c_str(), env, cf); + return cf; +} + +static void checkResult(CephFile a, CephFile b) +{ + EXPECT_STREQ(a.name.c_str(), b.name.c_str()); + EXPECT_STREQ(a.pool.c_str(), b.pool.c_str()); + EXPECT_STREQ(a.userId.c_str(), b.userId.c_str()); + EXPECT_EQ(a.nbStripes, b.nbStripes); + EXPECT_EQ(a.stripeUnit, b.stripeUnit); + EXPECT_EQ(a.objectSize, b.objectSize); +} + +TEST(ParsingTest, Parameters) +{ + std::map inputs; + + inputs[""] = (CephFile){"", "default", "admin", 1, 4 * MB, 4 * MB}; + inputs["pool"] = (CephFile){"", "pool", "admin", 1, 4 * MB, 4 * MB}; + inputs["@"] = (CephFile){"", "default", "", 1, 4 * MB, 4 * MB}; + inputs["@pool"] = (CephFile){"", "pool", "", 1, 4 * MB, 4 * MB}; + inputs["user@"] = (CephFile){"", "default", "user", 1, 4 * MB, 4 * MB}; + inputs["user@pool"] = (CephFile){"", "pool", "user", 1, 4 * MB, 4 * MB}; + inputs["pool,1"] = (CephFile){"", "pool", "admin", 1, 4 * MB, 4 * MB}; + inputs["user@pool,1"] = (CephFile){"", "pool", "user", 1, 4 * MB, 4 * MB}; + inputs["pool,5"] = (CephFile){"", "pool", "admin", 5, 4 * MB, 4 * MB}; + inputs["user@pool,5"] = (CephFile){"", "pool", "user", 5, 4 * MB, 4 * MB}; + inputs["pool,5,200"] = (CephFile){"", "pool", "admin", 5, 200, 4 * MB}; + inputs["user@pool,5,200"] = (CephFile){"", "pool", "user", 5, 200, 4 * MB}; + inputs["pool,5,200,800"] = (CephFile){"", "pool", "admin", 5, 200, 800}; + inputs["user@pool,5,200,800"] = (CephFile){"", "pool", "user", 5, 200, 800}; + + for (auto it = inputs.begin(); it != inputs.end(); it++) + checkResult(parseParam(it->first), it->second); +} + +TEST(ParsingTest, File) +{ + std::vector filenames; + std::map inputs; + + filenames.push_back(""); + filenames.push_back("foo"); + filenames.push_back("/foo/bar"); + filenames.push_back("foo@bar"); + filenames.push_back("foo@bar,1"); + filenames.push_back("foo@bar,1,2"); + filenames.push_back("foo@bar,1,2,3"); + filenames.push_back("foo:bar"); + filenames.push_back(":foo"); + + for (auto it = filenames.begin(); it != filenames.end(); it++) { + if (std::string::npos == it->find(':')) + inputs[*it] = (CephFile){*it, "default", "admin", 1, 4 * MB, 4 * MB}; + + inputs[":" + *it] = (CephFile){*it, "default", "admin", 1, 4 * MB, 4 * MB}; + inputs["pool:" + *it] = (CephFile){*it, "pool", "admin", 1, 4 * MB, 4 * MB}; + inputs["@:" + *it] = (CephFile){*it, "default", "", 1, 4 * MB, 4 * MB}; + inputs["@pool:" + *it] = (CephFile){*it, "pool", "", 1, 4 * MB, 4 * MB}; + inputs["user@:" + *it] = (CephFile){*it, "default", "user", 1, 4 * MB, 4 * MB}; + inputs["user@pool:" + *it] = (CephFile){*it, "pool", "user", 1, 4 * MB, 4 * MB}; + inputs["pool,1:" + *it] = (CephFile){*it, "pool", "admin", 1, 4 * MB, 4 * MB}; + inputs["user@pool,1:" + *it] = (CephFile){*it, "pool", "user", 1, 4 * MB, 4 * MB}; + inputs["pool,5:" + *it] = (CephFile){*it, "pool", "admin", 5, 4 * MB, 4 * MB}; + inputs["user@pool,5:" + *it] = (CephFile){*it, "pool", "user", 5, 4 * MB, 4 * MB}; + inputs["pool,5,200:" + *it] = (CephFile){*it, "pool", "admin", 5, 200, 4 * MB}; + inputs["user@pool,5,200:" + *it] = (CephFile){*it, "pool", "user", 5, 200, 4 * MB}; + inputs["pool,5,200,800:" + *it] = (CephFile){*it, "pool", "admin", 5, 200, 800}; + inputs["user@pool,5,200,800:" + *it] = (CephFile){*it, "pool", "user", 5, 200, 800}; + } + + for (auto it = inputs.begin(); it != inputs.end(); it++) + checkResult(parseFile(it->first), it->second); +} diff --git a/src/XrdCeph/tests/XrdCephTests/CMakeLists.txt b/src/XrdCeph/tests/XrdCephTests/CMakeLists.txt deleted file mode 100644 index 2011390d85b..00000000000 --- a/src/XrdCeph/tests/XrdCephTests/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -message( "XROOTD_INCLUDE_DIR : ${XROOTD_INCLUDE_DIR}" ) - -add_library( - XrdCephTests MODULE - CephParsingTest.cc -) - -target_link_libraries( - XrdCephTests - pthread - ${CPPUNIT_LIBRARIES} - ${ZLIB_LIBRARY} - XrdCephPosix ) - -target_include_directories(XrdCephTests PRIVATE - ${CPPUNIT_INCLUDE_DIRS} - ${RADOS_INCLUDE_DIR} - ${XROOTD_INCLUDE_DIR} - ${PROJECT_SOURCE_DIR}/src) - -#------------------------------------------------------------------------------- -# Install -#------------------------------------------------------------------------------- -install( - TARGETS XrdCephTests - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) diff --git a/src/XrdCeph/tests/XrdCephTests/CephParsingTest.cc b/src/XrdCeph/tests/XrdCephTests/CephParsingTest.cc deleted file mode 100644 index 194bd5e40c8..00000000000 --- a/src/XrdCeph/tests/XrdCephTests/CephParsingTest.cc +++ /dev/null @@ -1,149 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2011-2012 by European Organization for Nuclear Research (CERN) -// Author: Sebastien Ponce -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -//------------------------------------------------------------------------------ - -#include -#include -#include - -#define MB 1024*1024 -struct CephFile { - std::string name; - std::string pool; - std::string userId; - unsigned int nbStripes; - unsigned long long stripeUnit; - unsigned long long objectSize; -}; -void fillCephFileParams(const std::string ¶ms, XrdOucEnv *env, CephFile &file); -void fillCephFile(const char *path, XrdOucEnv *env, CephFile &file); - -//------------------------------------------------------------------------------ -// Declaration -//------------------------------------------------------------------------------ -class CephParsingTest: public CppUnit::TestCase -{ - public: - CPPUNIT_TEST_SUITE( CephParsingTest ); - CPPUNIT_TEST( ParamTest ); - CPPUNIT_TEST( FileTest ); - CPPUNIT_TEST_SUITE_END(); - void ParamTest(); - void FileTest(); -}; - -CPPUNIT_TEST_SUITE_REGISTRATION( CephParsingTest ); - -//------------------------------------------------------------------------------ -// Helper functions -//------------------------------------------------------------------------------ -static CephFile parseParam(std::string param, XrdOucEnv *env= NULL) { - CephFile cf; - fillCephFileParams(param, env, cf); - return cf; -} - -static CephFile parseFile(std::string param, XrdOucEnv *env= NULL) { - CephFile cf; - fillCephFile(param.c_str(), env, cf); - return cf; -} - -void checkResult(CephFile a, CephFile b) { - std::cout << a.name << " " << a.pool << " " << a.userId - << " " << a.nbStripes << " " << a.stripeUnit << " " << a.objectSize - << " / " << b.name << " " << b.pool << " " << b.userId - << " " << b.nbStripes << " " << b.stripeUnit << " " << b.objectSize - << std::endl; - CPPUNIT_ASSERT(a.name == b.name); - CPPUNIT_ASSERT(a.pool == b.pool); - CPPUNIT_ASSERT(a.userId == b.userId); - CPPUNIT_ASSERT(a.nbStripes == b.nbStripes); - CPPUNIT_ASSERT(a.stripeUnit == b.stripeUnit); - CPPUNIT_ASSERT(a.objectSize == b.objectSize); -} - -//------------------------------------------------------------------------------ -// Param test -//------------------------------------------------------------------------------ -void CephParsingTest::ParamTest() { - std::map inputs; - inputs[""] = (CephFile){"", "default", "admin", 1, 4*MB, 4*MB}; - inputs["pool"] = (CephFile){"", "pool", "admin", 1, 4*MB, 4*MB}; - inputs["@"] = (CephFile){"", "default", "", 1, 4*MB, 4*MB}; - inputs["@pool"] = (CephFile){"", "pool", "", 1, 4*MB, 4*MB}; - inputs["user@"] = (CephFile){"", "default", "user", 1, 4*MB, 4*MB}; - inputs["user@pool"] = (CephFile){"", "pool", "user", 1, 4*MB, 4*MB}; - inputs["pool,1"] = (CephFile){"", "pool", "admin", 1, 4*MB, 4*MB}; - inputs["user@pool,1"] = (CephFile){"", "pool", "user", 1, 4*MB, 4*MB}; - inputs["pool,5"] = (CephFile){"", "pool", "admin", 5, 4*MB, 4*MB}; - inputs["user@pool,5"] = (CephFile){"", "pool", "user", 5, 4*MB, 4*MB}; - inputs["pool,5,200"] = (CephFile){"", "pool", "admin", 5, 200, 4*MB}; - inputs["user@pool,5,200"] = (CephFile){"", "pool", "user", 5, 200, 4*MB}; - inputs["pool,5,200,800"] = (CephFile){"", "pool", "admin", 5, 200, 800}; - inputs["user@pool,5,200,800"] = (CephFile){"", "pool", "user", 5, 200, 800}; - for (std::map::const_iterator it = inputs.begin(); - it != inputs.end(); - it++) { - std::cout << it->first << std::endl; - checkResult(parseParam(it->first), it->second); - } -} - -//------------------------------------------------------------------------------ -// File test -//------------------------------------------------------------------------------ -void CephParsingTest::FileTest() { - std::map inputs; - std::vector filenames; - filenames.push_back(""); - filenames.push_back("foo"); - filenames.push_back("/foo/bar"); - filenames.push_back("foo@bar"); - filenames.push_back("foo@bar,1"); - filenames.push_back("foo@bar,1,2"); - filenames.push_back("foo@bar,1,2,3"); - filenames.push_back("foo:bar"); - filenames.push_back(":foo"); - for (std::vector::const_iterator it = filenames.begin(); - it != filenames.end(); - it++) { - if (std::string::npos == it->find(':')) { - inputs[*it] = (CephFile){*it, "default", "admin", 1, 4*MB, 4*MB}; - } - inputs[":" + *it] = (CephFile){*it, "default", "admin", 1, 4*MB, 4*MB}; - inputs["pool:" + *it] = (CephFile){*it, "pool", "admin", 1, 4*MB, 4*MB}; - inputs["@:" + *it] = (CephFile){*it, "default", "", 1, 4*MB, 4*MB}; - inputs["@pool:" + *it] = (CephFile){*it, "pool", "", 1, 4*MB, 4*MB}; - inputs["user@:" + *it] = (CephFile){*it, "default", "user", 1, 4*MB, 4*MB}; - inputs["user@pool:" + *it] = (CephFile){*it, "pool", "user", 1, 4*MB, 4*MB}; - inputs["pool,1:" + *it] = (CephFile){*it, "pool", "admin", 1, 4*MB, 4*MB}; - inputs["user@pool,1:" + *it] = (CephFile){*it, "pool", "user", 1, 4*MB, 4*MB}; - inputs["pool,5:" + *it] = (CephFile){*it, "pool", "admin", 5, 4*MB, 4*MB}; - inputs["user@pool,5:" + *it] = (CephFile){*it, "pool", "user", 5, 4*MB, 4*MB}; - inputs["pool,5,200:" + *it] = (CephFile){*it, "pool", "admin", 5, 200, 4*MB}; - inputs["user@pool,5,200:" + *it] = (CephFile){*it, "pool", "user", 5, 200, 4*MB}; - inputs["pool,5,200,800:" + *it] = (CephFile){*it, "pool", "admin", 5, 200, 800}; - inputs["user@pool,5,200,800:" + *it] = (CephFile){*it, "pool", "user", 5, 200, 800}; - } - for (std::map::const_iterator it = inputs.begin(); - it != inputs.end(); - it++) { - std::cout << it->first << std::endl; - checkResult(parseFile(it->first), it->second); - } -} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 070d7bf7dfd..b982b387e24 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -15,9 +15,5 @@ if( BUILD_XRDEC ) add_subdirectory( XrdEc ) # new tests with GTest endif() -if( BUILD_CEPH ) - add_subdirectory( XrdCephTests ) -endif() - add_subdirectory( XRootD ) add_subdirectory( cluster ) diff --git a/tests/XrdCephTests/CMakeLists.txt b/tests/XrdCephTests/CMakeLists.txt deleted file mode 100644 index ed05bc465d0..00000000000 --- a/tests/XrdCephTests/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -add_library( - XrdCephTests MODULE - CephParsingTest.cc -) - -target_link_libraries( - XrdCephTests - ${CMAKE_THREAD_LIBS_INIT} - ${CPPUNIT_LIBRARIES} - ZLIB::ZLIB - XrdCephPosix ) - -target_include_directories( XrdCephTests PRIVATE ${CPPUNIT_INCLUDE_DIRS} ) - -#------------------------------------------------------------------------------- -# Install -#------------------------------------------------------------------------------- -install( - TARGETS XrdCephTests - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) diff --git a/tests/XrdCephTests/CephParsingTest.cc b/tests/XrdCephTests/CephParsingTest.cc deleted file mode 100644 index 06cb3366097..00000000000 --- a/tests/XrdCephTests/CephParsingTest.cc +++ /dev/null @@ -1,149 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2011-2012 by European Organization for Nuclear Research (CERN) -// Author: Sebastien Ponce -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -//------------------------------------------------------------------------------ - -#include -#include -#include - -#define MB 1024*1024 -struct CephFile { - std::string name; - std::string pool; - std::string userId; - unsigned int nbStripes; - unsigned long long stripeUnit; - unsigned long long objectSize; -}; -void fillCephFileParams(const std::string ¶ms, XrdOucEnv *env, CephFile &file); -void fillCephFile(const char *path, XrdOucEnv *env, CephFile &file); - -//------------------------------------------------------------------------------ -// Declaration -//------------------------------------------------------------------------------ -class CephParsingTest: public CppUnit::TestCase -{ - public: - CPPUNIT_TEST_SUITE( CephParsingTest ); - CPPUNIT_TEST( ParamTest ); - CPPUNIT_TEST( FileTest ); - CPPUNIT_TEST_SUITE_END(); - void ParamTest(); - void FileTest(); -}; - -CPPUNIT_TEST_SUITE_REGISTRATION( CephParsingTest ); - -//------------------------------------------------------------------------------ -// Helper functions -//------------------------------------------------------------------------------ -static CephFile parseParam(std::string param, XrdOucEnv *env= NULL) { - CephFile cf; - fillCephFileParams(param, env, cf); - return cf; -} - -static CephFile parseFile(std::string param, XrdOucEnv *env= NULL) { - CephFile cf; - fillCephFile(param.c_str(), env, cf); - return cf; -} - -void checkResult(CephFile a, CephFile b) { - std::cout << a.name << " " << a.pool << " " << a.userId - << " " << a.nbStripes << " " << a.stripeUnit << " " << a.objectSize - << " / " << b.name << " " << b.pool << " " << b.userId - << " " << b.nbStripes << " " << b.stripeUnit << " " << b.objectSize - << std::endl; - CPPUNIT_ASSERT_EQUAL(a.name, b.name); - CPPUNIT_ASSERT_EQUAL(a.pool, b.pool); - CPPUNIT_ASSERT_EQUAL(a.userId, b.userId); - CPPUNIT_ASSERT_EQUAL(a.nbStripes, b.nbStripes); - CPPUNIT_ASSERT_EQUAL(a.stripeUnit, b.stripeUnit); - CPPUNIT_ASSERT_EQUAL(a.objectSize, b.objectSize); -} - -//------------------------------------------------------------------------------ -// Param test -//------------------------------------------------------------------------------ -void CephParsingTest::ParamTest() { - std::map inputs; - inputs[""] = (CephFile){"", "default", "admin", 1, 4*MB, 4*MB}; - inputs["pool"] = (CephFile){"", "pool", "admin", 1, 4*MB, 4*MB}; - inputs["@"] = (CephFile){"", "default", "", 1, 4*MB, 4*MB}; - inputs["@pool"] = (CephFile){"", "pool", "", 1, 4*MB, 4*MB}; - inputs["user@"] = (CephFile){"", "default", "user", 1, 4*MB, 4*MB}; - inputs["user@pool"] = (CephFile){"", "pool", "user", 1, 4*MB, 4*MB}; - inputs["pool,1"] = (CephFile){"", "pool", "admin", 1, 4*MB, 4*MB}; - inputs["user@pool,1"] = (CephFile){"", "pool", "user", 1, 4*MB, 4*MB}; - inputs["pool,5"] = (CephFile){"", "pool", "admin", 5, 4*MB, 4*MB}; - inputs["user@pool,5"] = (CephFile){"", "pool", "user", 5, 4*MB, 4*MB}; - inputs["pool,5,200"] = (CephFile){"", "pool", "admin", 5, 200, 4*MB}; - inputs["user@pool,5,200"] = (CephFile){"", "pool", "user", 5, 200, 4*MB}; - inputs["pool,5,200,800"] = (CephFile){"", "pool", "admin", 5, 200, 800}; - inputs["user@pool,5,200,800"] = (CephFile){"", "pool", "user", 5, 200, 800}; - for (std::map::const_iterator it = inputs.begin(); - it != inputs.end(); - it++) { - std::cout << it->first << std::endl; - checkResult(parseParam(it->first), it->second); - } -} - -//------------------------------------------------------------------------------ -// File test -//------------------------------------------------------------------------------ -void CephParsingTest::FileTest() { - std::map inputs; - std::vector filenames; - filenames.push_back(""); - filenames.push_back("foo"); - filenames.push_back("/foo/bar"); - filenames.push_back("foo@bar"); - filenames.push_back("foo@bar,1"); - filenames.push_back("foo@bar,1,2"); - filenames.push_back("foo@bar,1,2,3"); - filenames.push_back("foo:bar"); - filenames.push_back(":foo"); - for (std::vector::const_iterator it = filenames.begin(); - it != filenames.end(); - it++) { - if (std::string::npos == it->find(':')) { - inputs[*it] = (CephFile){*it, "default", "admin", 1, 4*MB, 4*MB}; - } - inputs[":" + *it] = (CephFile){*it, "default", "admin", 1, 4*MB, 4*MB}; - inputs["pool:" + *it] = (CephFile){*it, "pool", "admin", 1, 4*MB, 4*MB}; - inputs["@:" + *it] = (CephFile){*it, "default", "", 1, 4*MB, 4*MB}; - inputs["@pool:" + *it] = (CephFile){*it, "pool", "", 1, 4*MB, 4*MB}; - inputs["user@:" + *it] = (CephFile){*it, "default", "user", 1, 4*MB, 4*MB}; - inputs["user@pool:" + *it] = (CephFile){*it, "pool", "user", 1, 4*MB, 4*MB}; - inputs["pool,1:" + *it] = (CephFile){*it, "pool", "admin", 1, 4*MB, 4*MB}; - inputs["user@pool,1:" + *it] = (CephFile){*it, "pool", "user", 1, 4*MB, 4*MB}; - inputs["pool,5:" + *it] = (CephFile){*it, "pool", "admin", 5, 4*MB, 4*MB}; - inputs["user@pool,5:" + *it] = (CephFile){*it, "pool", "user", 5, 4*MB, 4*MB}; - inputs["pool,5,200:" + *it] = (CephFile){*it, "pool", "admin", 5, 200, 4*MB}; - inputs["user@pool,5,200:" + *it] = (CephFile){*it, "pool", "user", 5, 200, 4*MB}; - inputs["pool,5,200,800:" + *it] = (CephFile){*it, "pool", "admin", 5, 200, 800}; - inputs["user@pool,5,200,800:" + *it] = (CephFile){*it, "pool", "user", 5, 200, 800}; - } - for (std::map::const_iterator it = inputs.begin(); - it != inputs.end(); - it++) { - std::cout << it->first << std::endl; - checkResult(parseFile(it->first), it->second); - } -} From 06c9432dc6565eda4083f76cbebd9094a418e819 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Thu, 8 Feb 2024 15:32:29 +0100 Subject: [PATCH 013/276] [XrdCeph] Update CMake build system to be like other plugins --- .ci/config.cmake | 1 + {src/XrdCeph/cmake => cmake}/Findceph.cmake | 2 +- cmake/XRootDDefaults.cmake | 6 + cmake/XRootDSummary.cmake | 2 +- debian/rules | 4 +- src/CMakeLists.txt | 4 +- src/XrdCeph/.gitattributes | 1 - src/XrdCeph/.gitignore | 58 - src/XrdCeph/.gitlab-ci.yml | 75 - src/XrdCeph/.travis.yml | 22 - src/XrdCeph/CMakeLists.txt | 100 +- src/XrdCeph/COPYING | 674 ------- src/XrdCeph/COPYING.BSD | 35 - src/XrdCeph/COPYING.LGPL | 165 -- src/XrdCeph/Doxyfile | 316 --- src/XrdCeph/LICENSE | 18 - src/XrdCeph/README | 54 - src/XrdCeph/VERSION_INFO | 1 - src/XrdCeph/{src/XrdCeph => }/XrdCephOss.cc | 13 +- src/XrdCeph/{src/XrdCeph => }/XrdCephOss.hh | 2 + .../{src/XrdCeph => }/XrdCephOssDir.cc | 0 .../{src/XrdCeph => }/XrdCephOssDir.hh | 0 .../{src/XrdCeph => }/XrdCephOssFile.cc | 0 .../{src/XrdCeph => }/XrdCephOssFile.hh | 0 src/XrdCeph/{src/XrdCeph => }/XrdCephPosix.cc | 0 src/XrdCeph/{src/XrdCeph => }/XrdCephPosix.hh | 0 src/XrdCeph/{src/XrdCeph => }/XrdCephXAttr.cc | 0 src/XrdCeph/{src/XrdCeph => }/XrdCephXAttr.hh | 0 src/XrdCeph/cmake/FindCppUnit.cmake | 30 - src/XrdCeph/cmake/FindXRootD.cmake | 35 - src/XrdCeph/cmake/GNUInstallDirs.cmake | 182 -- src/XrdCeph/cmake/XRootDDefaults.cmake | 17 - src/XrdCeph/cmake/XRootDFindLibs.cmake | 16 - src/XrdCeph/cmake/XRootDOSDefs.cmake | 44 - src/XrdCeph/cmake/XRootDSummary.cmake | 21 - src/XrdCeph/cmake/XRootDUtils.cmake | 39 - src/XrdCeph/docs/PreReleaseNotes.txt | 8 - src/XrdCeph/docs/ReleaseNotes.txt | 1692 ----------------- src/XrdCeph/packaging/debian/compat | 1 - src/XrdCeph/packaging/debian/control | 48 - src/XrdCeph/packaging/debian/copyright | 18 - src/XrdCeph/packaging/debian/rules | 12 - src/XrdCeph/packaging/debian/source/format | 1 - .../debian/xrootd-client-devel.install | 9 - .../debian/xrootd-client-libs.install | 8 - .../debian/xrootd-client-libs.postinst | 3 - .../debian/xrootd-client-libs.postrm | 3 - .../packaging/debian/xrootd-client.install | 18 - .../packaging/debian/xrootd-devel.install | 15 - .../packaging/debian/xrootd-libs.install | 9 - .../packaging/debian/xrootd-libs.postinst | 3 - .../packaging/debian/xrootd-libs.postrm | 3 - .../debian/xrootd-private-devel.install | 3 - .../debian/xrootd-server-devel.install | 8 - .../debian/xrootd-server-libs.install | 14 - .../debian/xrootd-server-libs.postinst | 3 - .../debian/xrootd-server-libs.postrm | 3 - .../debian_scripts/publish_debian_cern.sh | 35 - src/XrdCeph/packaging/makesrpm.sh | 256 --- .../packaging/rhel/xrootd-ceph.spec.in | 167 -- src/XrdCeph/src/CMakeLists.txt | 10 - src/XrdCeph/src/XrdCeph.cmake | 74 - src/XrdCeph/src/XrdVersion.hh.in | 106 -- tests/CMakeLists.txt | 1 + .../tests => tests/XrdCeph}/CMakeLists.txt | 6 +- .../tests => tests/XrdCeph}/XrdCeph.cc | 0 xrootd.spec | 2 +- 67 files changed, 69 insertions(+), 4407 deletions(-) rename {src/XrdCeph/cmake => cmake}/Findceph.cmake (86%) delete mode 100644 src/XrdCeph/.gitattributes delete mode 100644 src/XrdCeph/.gitignore delete mode 100644 src/XrdCeph/.gitlab-ci.yml delete mode 100644 src/XrdCeph/.travis.yml delete mode 100644 src/XrdCeph/COPYING delete mode 100644 src/XrdCeph/COPYING.BSD delete mode 100644 src/XrdCeph/COPYING.LGPL delete mode 100644 src/XrdCeph/Doxyfile delete mode 100644 src/XrdCeph/LICENSE delete mode 100644 src/XrdCeph/README delete mode 100644 src/XrdCeph/VERSION_INFO rename src/XrdCeph/{src/XrdCeph => }/XrdCephOss.cc (99%) rename src/XrdCeph/{src/XrdCeph => }/XrdCephOss.hh (99%) rename src/XrdCeph/{src/XrdCeph => }/XrdCephOssDir.cc (100%) rename src/XrdCeph/{src/XrdCeph => }/XrdCephOssDir.hh (100%) rename src/XrdCeph/{src/XrdCeph => }/XrdCephOssFile.cc (100%) rename src/XrdCeph/{src/XrdCeph => }/XrdCephOssFile.hh (100%) rename src/XrdCeph/{src/XrdCeph => }/XrdCephPosix.cc (100%) rename src/XrdCeph/{src/XrdCeph => }/XrdCephPosix.hh (100%) rename src/XrdCeph/{src/XrdCeph => }/XrdCephXAttr.cc (100%) rename src/XrdCeph/{src/XrdCeph => }/XrdCephXAttr.hh (100%) delete mode 100644 src/XrdCeph/cmake/FindCppUnit.cmake delete mode 100644 src/XrdCeph/cmake/FindXRootD.cmake delete mode 100644 src/XrdCeph/cmake/GNUInstallDirs.cmake delete mode 100644 src/XrdCeph/cmake/XRootDDefaults.cmake delete mode 100644 src/XrdCeph/cmake/XRootDFindLibs.cmake delete mode 100644 src/XrdCeph/cmake/XRootDOSDefs.cmake delete mode 100644 src/XrdCeph/cmake/XRootDSummary.cmake delete mode 100644 src/XrdCeph/cmake/XRootDUtils.cmake delete mode 100644 src/XrdCeph/docs/PreReleaseNotes.txt delete mode 100644 src/XrdCeph/docs/ReleaseNotes.txt delete mode 100644 src/XrdCeph/packaging/debian/compat delete mode 100644 src/XrdCeph/packaging/debian/control delete mode 100644 src/XrdCeph/packaging/debian/copyright delete mode 100755 src/XrdCeph/packaging/debian/rules delete mode 100644 src/XrdCeph/packaging/debian/source/format delete mode 100644 src/XrdCeph/packaging/debian/xrootd-client-devel.install delete mode 100644 src/XrdCeph/packaging/debian/xrootd-client-libs.install delete mode 100644 src/XrdCeph/packaging/debian/xrootd-client-libs.postinst delete mode 100644 src/XrdCeph/packaging/debian/xrootd-client-libs.postrm delete mode 100644 src/XrdCeph/packaging/debian/xrootd-client.install delete mode 100644 src/XrdCeph/packaging/debian/xrootd-devel.install delete mode 100644 src/XrdCeph/packaging/debian/xrootd-libs.install delete mode 100644 src/XrdCeph/packaging/debian/xrootd-libs.postinst delete mode 100644 src/XrdCeph/packaging/debian/xrootd-libs.postrm delete mode 100644 src/XrdCeph/packaging/debian/xrootd-private-devel.install delete mode 100644 src/XrdCeph/packaging/debian/xrootd-server-devel.install delete mode 100644 src/XrdCeph/packaging/debian/xrootd-server-libs.install delete mode 100644 src/XrdCeph/packaging/debian/xrootd-server-libs.postinst delete mode 100644 src/XrdCeph/packaging/debian/xrootd-server-libs.postrm delete mode 100755 src/XrdCeph/packaging/debian_scripts/publish_debian_cern.sh delete mode 100755 src/XrdCeph/packaging/makesrpm.sh delete mode 100644 src/XrdCeph/packaging/rhel/xrootd-ceph.spec.in delete mode 100644 src/XrdCeph/src/CMakeLists.txt delete mode 100644 src/XrdCeph/src/XrdCeph.cmake delete mode 100644 src/XrdCeph/src/XrdVersion.hh.in rename {src/XrdCeph/tests => tests/XrdCeph}/CMakeLists.txt (79%) rename {src/XrdCeph/tests => tests/XrdCeph}/XrdCeph.cc (100%) diff --git a/.ci/config.cmake b/.ci/config.cmake index 1da3b58ada5..38573e6a7c1 100644 --- a/.ci/config.cmake +++ b/.ci/config.cmake @@ -1,6 +1,7 @@ set(FORCE_ENABLED 0 CACHE BOOL "") set(ENABLE_ASAN 0 CACHE BOOL "") set(ENABLE_TSAN 0 CACHE BOOL "") +set(ENABLE_CEPH 1 CACHE BOOL "") set(ENABLE_FUSE 1 CACHE BOOL "") set(ENABLE_HTTP 1 CACHE BOOL "") set(ENABLE_KRB5 1 CACHE BOOL "") diff --git a/src/XrdCeph/cmake/Findceph.cmake b/cmake/Findceph.cmake similarity index 86% rename from src/XrdCeph/cmake/Findceph.cmake rename to cmake/Findceph.cmake index 661891de850..d90d2879056 100644 --- a/src/XrdCeph/cmake/Findceph.cmake +++ b/cmake/Findceph.cmake @@ -40,4 +40,4 @@ find_library( set(RADOS_LIBS ${RADOS_LIB} ${RADOSSTRIPER_LIB}) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(ceph DEFAULT_MSG RADOS_INCLUDE_DIR RADOS_LIBS) +find_package_handle_standard_args(ceph REQUIRED_VARS RADOS_INCLUDE_DIR RADOS_LIB RADOSSTRIPER_LIB) diff --git a/cmake/XRootDDefaults.cmake b/cmake/XRootDDefaults.cmake index f7223ceadd2..4dd9af38f4d 100644 --- a/cmake/XRootDDefaults.cmake +++ b/cmake/XRootDDefaults.cmake @@ -12,6 +12,7 @@ endif() include( CMakeDependentOption ) define_default( PLUGIN_VERSION 5 ) +option( ENABLE_CEPH "Enable XrdCeph plugins." FALSE ) option( ENABLE_FUSE "Enable the fuse filesystem driver if possible." TRUE ) option( ENABLE_KRB5 "Enable the Kerberos 5 authentication if possible." TRUE ) option( ENABLE_READLINE "Enable the lib readline support in the commandline utilities." TRUE ) @@ -32,3 +33,8 @@ cmake_dependent_option( ENABLE_MACAROONS "Enable Macaroons plugin." TRUE "NOT XR option( FORCE_ENABLED "Fail build if enabled components cannot be built." FALSE ) cmake_dependent_option( USE_SYSTEM_ISAL "Use isa-l installed in the system" FALSE "ENABLE_XRDEC" FALSE ) define_default( XRD_PYTHON_REQ_VERSION 3 ) + +# backward compatibility +if(XRDCEPH_SUBMODULE) + set(ENABLE_CEPH TRUE) +endif() diff --git a/cmake/XRootDSummary.cmake b/cmake/XRootDSummary.cmake index 7df55dda4e5..c7bd4db2f37 100644 --- a/cmake/XRootDSummary.cmake +++ b/cmake/XRootDSummary.cmake @@ -2,7 +2,7 @@ # Print the configuration summary #------------------------------------------------------------------------------- set( TRUE_VAR TRUE ) -component_status( CEPH XRDCEPH_SUBMODULE TRUE_VAR) +component_status( CEPH ENABLE_CEPH BUILD_CEPH ) component_status( FUSE BUILD_FUSE FUSE_FOUND ) component_status( HTTP BUILD_HTTP OPENSSL_FOUND ) component_status( KRB5 BUILD_KRB5 KERBEROS5_FOUND ) diff --git a/debian/rules b/debian/rules index 479f83d1fdc..ee90c9e4f9c 100755 --- a/debian/rules +++ b/debian/rules @@ -10,6 +10,7 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all optimize=-lto override_dh_auto_configure: dh_auto_configure -- \ + -DENABLE_CEPH:BOOL=1 \ -DENABLE_FUSE:BOOL=1 \ -DENABLE_HTTP:BOOL=1 \ -DENABLE_KRB5:BOOL=1 \ @@ -23,8 +24,7 @@ override_dh_auto_configure: -DENABLE_TESTS:BOOL=1 \ -DFORCE_ENABLED:BOOL=1 \ -DINSTALL_PYTHON_BINDINGS:BOOL=0 \ - -DUSE_SYSTEM_ISAL:BOOL=1 \ - -DXRDCEPH_SUBMODULE:BOOL=1 + -DUSE_SYSTEM_ISAL:BOOL=1 override_dh_auto_build: dh_auto_build diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 69add8bd96f..39d7ae578b4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -70,9 +70,7 @@ if( NOT XRDCL_ONLY ) include( XrdVoms ) endif() - if( XRDCEPH_SUBMODULE ) - add_subdirectory( XrdCeph ) - endif() + add_subdirectory( XrdCeph ) if( BUILD_SCITOKENS ) include( XrdSciTokens ) diff --git a/src/XrdCeph/.gitattributes b/src/XrdCeph/.gitattributes deleted file mode 100644 index 0893fe6afee..00000000000 --- a/src/XrdCeph/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -VERSION_INFO export-subst diff --git a/src/XrdCeph/.gitignore b/src/XrdCeph/.gitignore deleted file mode 100644 index 9bf47afb3b4..00000000000 --- a/src/XrdCeph/.gitignore +++ /dev/null @@ -1,58 +0,0 @@ -*.o -*.lo -.libs -.deps -Makefile -Makefile.in -*.la -GNUmakefile.classic -aclocal.m4 -autom4te.cache/ -compile -config/GNUmake.rules.sunCC -config/GNUmake.rules.sunCCamd -config/GNUmake.rules.sunCCamd510 -config/GNUmake.rules.sunCCamd64 -config/GNUmake.rules.sunCCi86pc -config.guess -config.log -config.status -config.sub -configure -depcomp -install-sh -lib/ -libtool -ltmain.sh -missing -src/GNUmake.env -src/GNUmake.options -src/Makefile_include -src/XrdAcc/XrdAccTest -src/XrdApps/mpxstats -src/XrdApps/wait41 -src/XrdApps/xrdadler32 -src/XrdClient/TestXrdClient -src/XrdClient/TestXrdClient_read -src/XrdClient/XrdClientAdmin_c_wrap.cc -src/XrdClient/xprep -src/XrdClient/xrd -src/XrdClient/xrdcp -src/XrdClient/xrdstagetool -src/XrdCms/cmsd -src/XrdCns/XrdCnsd -src/XrdCns/cns_ssi -src/XrdFrm/frm_admin -src/XrdFrm/frm_purged -src/XrdFrm/frm_xfragent -src/XrdFrm/frm_xfrd -src/XrdSec/testclient -src/XrdSec/testserver -src/XrdSecgsi/xrdgsiproxy -src/XrdSecpwd/xrdpwdadmin -src/XrdSecssl/xrdsecssltest -src/XrdSecsss/xrdsssadmin -src/XrdXrootd/xrootd -test/testconfig.sh -xrootd.spec - diff --git a/src/XrdCeph/.gitlab-ci.yml b/src/XrdCeph/.gitlab-ci.yml deleted file mode 100644 index 3b097867e8e..00000000000 --- a/src/XrdCeph/.gitlab-ci.yml +++ /dev/null @@ -1,75 +0,0 @@ -stages: - - build:rpm - -release:cc7:ceph: - stage: build:rpm - image: gitlab-registry.cern.ch/linuxsupport/cc7-base - script: - - yum install --nogpg -y cmake3 make gcc-c++ rpm-build which git yum-plugin-priorities sssd-client sudo createrepo - - git checkout tags/${CI_COMMIT_TAG} - - cd packaging/ - - ./makesrpm.sh --define "dist .el7" - - echo -e '[ceph]\nname=ceph\nbaseurl=http://linuxsoft.cern.ch/mirror/download.ceph.com/rpm-nautilus/el7/x86_64/\npriority=4\ngpgcheck=0\nenabled=1\n' >> /etc/yum.repos.d/ceph.repo - - echo -e '[xrootd-testing]\nname=XRootD Testing repository\nbaseurl=http://xrootd.org/binaries/testing/slc/7/$basearch http://xrootd.cern.ch/sw/repos/testing/slc/7/$basearch\ngpgcheck=1\nenabled=1\nprotect=0\ngpgkey=http://xrootd.cern.ch/sw/releases/RPM-GPG-KEY.txt\n' >> /etc/yum.repos.d/xrootd-testing.repo - - echo -e '[xrootd-stable]\nname=XRootD Stable repository\nbaseurl=http://xrootd.org/binaries/stable/slc/7/$basearch http://xrootd.cern.ch/sw/repos/stable/slc/7/$basearch\ngpgcheck=1\nenabled=1\nprotect=0\ngpgkey=http://xrootd.cern.ch/sw/releases/RPM-GPG-KEY.txt\n' >> /etc/yum.repos.d/xrootd-stable.repo - - yum-builddep --setopt=cern*.exclude=xrootd* --nogpgcheck -y *.src.rpm - - rpmbuild --rebuild --define "_rpmdir RPMS/" --define "_build_name_fmt %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" -D "dist .el7" *.src.rpm - - repo=/eos/project/s/storage-ci/www/xrootd/ceph-release/cc-7/x86_64/ - - sudo -u stci -H mkdir -p $repo - - sudo -u stci -H cp *.src.rpm $repo - - sudo -u stci -H cp RPMS/* $repo - - sudo -u stci -H createrepo --update -q $repo - tags: - - docker_node - only: - - tags - except: - - schedules - -weekly:cc7:ceph: - stage: build:rpm - image: gitlab-registry.cern.ch/linuxsupport/cc7-base - script: - - yum install --nogpg -y cmake3 make gcc-c++ rpm-build which git yum-plugin-priorities sssd-client sudo createrepo - - cd packaging/ - - echo -e '[ceph]\nname=ceph\nbaseurl=http://linuxsoft.cern.ch/mirror/download.ceph.com/rpm-nautilus/el7/x86_64/\npriority=4\ngpgcheck=0\nenabled=1\n' >> /etc/yum.repos.d/ceph.repo - - echo -e '[xrootd-experimental]\nname=XRootD Experimental repository\nbaseurl=http://storage-ci.web.cern.ch/storage-ci/xrootd/experimental/epel-7/$basearch\ngpgcheck=1\nenabled=1\nprotect=0\n' >> /etc/yum.repos.d/xrootd-experimental.repo - - yum clean all - - version=$(yum info xrootd-devel | grep Version | cut -d':' -f2 | tr -d "[:blank:]") - - release=$(yum info xrootd-devel | grep Release | cut -d':' -f2 | tr -d "[:blank:]") - - release=${release%.el7.cern} - - ./makesrpm.sh --version "$version-$release" - - yum-builddep --setopt=cern*.exclude=xrootd* --nogpgcheck -y *.src.rpm - - rpmbuild --rebuild --define "_rpmdir RPMS/" --define "_build_name_fmt %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" *.src.rpm - tags: - - docker_node - only: - - schedules - -build:cc7:ceph: - stage: build:rpm - image: gitlab-registry.cern.ch/linuxsupport/cc7-base - script: - - yum install --nogpg -y cmake3 make gcc-c++ rpm-build which git yum-plugin-priorities sssd-client sudo createrepo - - cd packaging/ - - echo -e '[ceph]\nname=ceph\nbaseurl=http://linuxsoft.cern.ch/mirror/download.ceph.com/rpm-nautilus/el7/x86_64/\npriority=4\ngpgcheck=0\nenabled=1\n' >> /etc/yum.repos.d/ceph.repo - - echo -e '[xrootd-experimental]\nname=XRootD Experimental repository\nbaseurl=http://storage-ci.web.cern.ch/storage-ci/xrootd/experimental/epel-7/$basearch\ngpgcheck=1\nenabled=1\nprotect=0\n' >> /etc/yum.repos.d/xrootd-experimental.repo - - yum clean all - - version=$(yum info xrootd-devel | grep Version | cut -d':' -f2 | tr -d "[:blank:]") - - release=$(yum info xrootd-devel | grep Release | cut -d':' -f2 | tr -d "[:blank:]") - - release=${release%.el7.cern} - - ./makesrpm.sh --version "$version-$release" - - yum-builddep --setopt=cern*.exclude=xrootd* --nogpgcheck -y *.src.rpm - - rpmbuild --rebuild --define "_rpmdir RPMS/" --define "_build_name_fmt %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" *.src.rpm - - path=/eos/project/s/storage-ci/www/xrootd/ceph/cc-7/x86_64/$(date +'%Y%m%d') - - sudo -u stci -H mkdir -p $path; - - sudo -u stci -H find ${path} -type f -name '*.rpm' -delete; - - sudo -u stci -H cp RPMS/* $path; - - sudo -u stci -H createrepo --update -q $path; - tags: - - docker_node - only: - - master - except: - - tags - diff --git a/src/XrdCeph/.travis.yml b/src/XrdCeph/.travis.yml deleted file mode 100644 index 0f57d7cb54e..00000000000 --- a/src/XrdCeph/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -sudo: false -dist: trusty -addons: - apt: - packages: - - libxml2-dev - - libcppunit-dev -language: cpp -compiler: - - clang - - gcc -script: - - mkdir build - - pushd build - - cmake -DCMAKE_INSTALL_PREFIX=$HOME/xrootd -DENABLE_TESTS=1 .. - - make - - make install - - popd -#after_script: -# - pushd build -# - ./tests/common/text-runner ./tests/XrdClTests/libXrdClTests.so 'All Tests' -# - popd diff --git a/src/XrdCeph/CMakeLists.txt b/src/XrdCeph/CMakeLists.txt index c8730143079..ff63a0526b9 100644 --- a/src/XrdCeph/CMakeLists.txt +++ b/src/XrdCeph/CMakeLists.txt @@ -1,62 +1,52 @@ -#------------------------------------------------------------------------------- -# Project description -#------------------------------------------------------------------------------- -cmake_minimum_required(VERSION 3.16...3.25) - -project( xrootd-ceph ) - -set( CMAKE_MODULE_PATH - ${PROJECT_SOURCE_DIR}/src - ${PROJECT_SOURCE_DIR}/cmake ) +if(NOT ENABLE_CEPH) + unset(BUILD_CEPH CACHE) + return() +endif() -if( NOT XRDCEPH_SUBMODULE ) - if(NOT (CMAKE_VERSION VERSION_LESS "3.1")) - cmake_policy(SET CMP0054 OLD) +if(FORCE_ENABLED) + find_package(ceph REQUIRED) +else() + find_package(ceph) + if(NOT CEPH_FOUND) + unset(BUILD_CEPH CACHE) + return() endif() endif() -include( XRootDUtils ) -CheckBuildDirectory() - -include( XRootDOSDefs ) -include( XRootDDefaults ) -include( XRootDFindLibs ) - -add_definitions( -DXRDPLUGIN_SOVERSION="${PLUGIN_VERSION}" ) - -#------------------------------------------------------------------------------- -# Generate the version header -#------------------------------------------------------------------------------- -if( NOT XRDCEPH_SUBMODULE ) - execute_process( - COMMAND ${CMAKE_SOURCE_DIR}/genversion.sh --print-only ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE XROOTD_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE ) - - add_custom_target( - XrdVersion.hh - ${CMAKE_SOURCE_DIR}/genversion.sh ${CMAKE_SOURCE_DIR} ) - - # sigh, yet another ugly hack :( - macro( add_library _target ) - _add_library( ${_target} ${ARGN} ) - add_dependencies( ${_target} XrdVersion.hh ) - endmacro() - - macro( add_executable _target ) - _add_executable( ${_target} ${ARGN} ) - add_dependencies( ${_target} XrdVersion.hh ) - endmacro() -endif() +set(BUILD_CEPH TRUE CACHE BOOL INTERNAL FORCE) -#------------------------------------------------------------------------------- -# Build in subdirectories -#------------------------------------------------------------------------------- -add_subdirectory( src ) +add_library(XrdCephPosix SHARED + XrdCephPosix.cc XrdCephPosix.hh) -if( BUILD_TESTS ) - ENABLE_TESTING() - add_subdirectory( tests ) -endif() +target_compile_options(XrdCephPosix + PRIVATE -Wno-deprecated-declarations) + +target_link_libraries(XrdCephPosix + PRIVATE XrdUtils ${RADOS_LIBS}) + +target_include_directories(XrdCephPosix + PUBLIC ${RADOS_INCLUDE_DIR} $) + +set_target_properties(XrdCephPosix + PROPERTIES VERSION 0.0.1 SOVERSION 0) + +set(LIB_XRD_CEPH XrdCeph-${PLUGIN_VERSION}) + +add_library(${LIB_XRD_CEPH} MODULE + XrdCephOss.cc XrdCephOss.hh + XrdCephOssFile.cc XrdCephOssFile.hh + XrdCephOssDir.cc XrdCephOssDir.hh) + +target_link_libraries(${LIB_XRD_CEPH} + PRIVATE ${XROOTD_LIBRARIES} XrdCephPosix) + +set(LIB_XRD_CEPH_XATTR XrdCephXattr-${PLUGIN_VERSION}) + +add_library(${LIB_XRD_CEPH_XATTR} MODULE + XrdCephXAttr.cc XrdCephXAttr.hh) + +target_link_libraries(${LIB_XRD_CEPH_XATTR} + PRIVATE ${XROOTD_LIBRARIES} XrdCephPosix) -include( XRootDSummary ) +install(TARGETS XrdCephPosix ${LIB_XRD_CEPH} ${LIB_XRD_CEPH_XATTR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/src/XrdCeph/COPYING b/src/XrdCeph/COPYING deleted file mode 100644 index 94a9ed024d3..00000000000 --- a/src/XrdCeph/COPYING +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/src/XrdCeph/COPYING.BSD b/src/XrdCeph/COPYING.BSD deleted file mode 100644 index ea8ff31a74e..00000000000 --- a/src/XrdCeph/COPYING.BSD +++ /dev/null @@ -1,35 +0,0 @@ -******************************************************************************** -*Prior to September 2nd, 2012 the XRootD software suite was licensed under a * -*modified BSD license shown below. This applies to all code that was in the * -*XRootD git repository prior to that date. All code is now licensed under LGPL.* -* See files LICENSE, COPYING.LGPL, and COPYING for license details. * -******************************************************************************** - -Copyright (c) 2005-2012, Board of Trustees of the Leland Stanford, Jr. University. -Produced under contract DE-AC02-76-SF00515 with the US Department of Energy. -All rights reserved. - Conditions of Use -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: -a. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -b. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. -c. Neither the name of the Leland Stanford, Jr. University nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. -d. Products derived from this software that do not adhere to the xrootd or cmsd - protocol specifications may not use the acronyms 'cmsd', 'Scalla', 'xroot', - and 'xrootd', regardless of capitalization, to describe such derivative works. - DISCLAIMER -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED -OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/XrdCeph/COPYING.LGPL b/src/XrdCeph/COPYING.LGPL deleted file mode 100644 index 65c5ca88a67..00000000000 --- a/src/XrdCeph/COPYING.LGPL +++ /dev/null @@ -1,165 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/src/XrdCeph/Doxyfile b/src/XrdCeph/Doxyfile deleted file mode 100644 index 0531e916456..00000000000 --- a/src/XrdCeph/Doxyfile +++ /dev/null @@ -1,316 +0,0 @@ -# Doxyfile 1.3.7 - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- -PROJECT_NAME = xrootd -PROJECT_NUMBER = -OUTPUT_DIRECTORY = doxydoc -CREATE_SUBDIRS = NO -OUTPUT_LANGUAGE = English -USE_WINDOWS_ENCODING = NO -BRIEF_MEMBER_DESC = YES -REPEAT_BRIEF = YES -ABBREVIATE_BRIEF = -ALWAYS_DETAILED_SEC = NO -INLINE_INHERITED_MEMB = NO -FULL_PATH_NAMES = NO -STRIP_FROM_PATH = -STRIP_FROM_INC_PATH = -SHORT_NAMES = NO -JAVADOC_AUTOBRIEF = NO -MULTILINE_CPP_IS_BRIEF = NO -DETAILS_AT_TOP = NO -INHERIT_DOCS = YES -DISTRIBUTE_GROUP_DOC = NO -TAB_SIZE = 8 -ALIASES = -OPTIMIZE_OUTPUT_FOR_C = NO -OPTIMIZE_OUTPUT_JAVA = NO -SUBGROUPING = YES -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- -EXTRACT_ALL = YES -EXTRACT_PRIVATE = YES -EXTRACT_STATIC = YES -EXTRACT_LOCAL_CLASSES = YES -EXTRACT_LOCAL_METHODS = NO -HIDE_UNDOC_MEMBERS = NO -HIDE_UNDOC_CLASSES = NO -HIDE_FRIEND_COMPOUNDS = NO -HIDE_IN_BODY_DOCS = NO -INTERNAL_DOCS = NO -CASE_SENSE_NAMES = YES -HIDE_SCOPE_NAMES = NO -SHOW_INCLUDE_FILES = YES -INLINE_INFO = YES -SORT_MEMBER_DOCS = YES -SORT_BRIEF_DOCS = NO -SORT_BY_SCOPE_NAME = NO -GENERATE_TODOLIST = YES -GENERATE_TESTLIST = YES -GENERATE_BUGLIST = YES -GENERATE_DEPRECATEDLIST= YES -ENABLED_SECTIONS = -MAX_INITIALIZER_LINES = 30 -SHOW_USED_FILES = YES -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- -QUIET = NO -WARNINGS = YES -WARN_IF_UNDOCUMENTED = YES -WARN_IF_DOC_ERROR = YES -WARN_FORMAT = "$file:$line: $text" -WARN_LOGFILE = -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- -INPUT = \ -src/XrdSec/XrdSecEntity.hh \ -src/XrdSec/XrdSecInterface.hh \ -src/Xrd/XrdJob.hh \ -src/Xrd/XrdBuffer.hh \ -src/Xrd/XrdScheduler.hh \ -src/Xrd/XrdLink.hh \ -src/Xrd/XrdLinkMatch.hh \ -src/Xrd/XrdProtocol.hh \ -src/XrdXrootd/XrdXrootdMonData.hh \ -src/XrdVersionPlugin.hh \ -src/XrdCks/XrdCksData.hh \ -src/XrdCks/XrdCksManager.hh \ -src/XrdCks/XrdCksCalc.hh \ -src/XrdCks/XrdCks.hh \ -src/XrdSfs/XrdSfsInterface.hh \ -src/XrdSfs/XrdSfsAio.hh \ -src/XrdNet/XrdNet.hh \ -src/XrdNet/XrdNetCmsNotify.hh \ -src/XrdNet/XrdNetConnect.hh \ -src/XrdNet/XrdNetOpts.hh \ -src/XrdNet/XrdNetSocket.hh \ -src/XrdSys/XrdSysError.hh \ -src/XrdSys/XrdSysPlatform.hh \ -src/XrdSys/XrdSysLogger.hh \ -src/XrdSys/XrdSysPthread.hh \ -src/XrdSys/XrdSysTimer.hh \ -src/XrdSys/XrdSysHeaders.hh \ -src/XrdSys/XrdSysDNS.hh \ -src/XrdSys/XrdSysXSLock.hh \ -src/XrdSys/XrdSysIOEvents.hh \ -src/XrdSys/XrdSysAtomics.hh \ -src/XrdSys/XrdSysPlugin.hh \ -src/XrdSys/XrdSysSemWait.hh \ -src/XrdClient/XrdClientConst.hh \ -src/XrdClient/XrdClientVector.hh \ -src/XrdClient/XrdClientAbs.hh \ -src/XrdClient/XrdClientAbsMonIntf.hh \ -src/XrdClient/XrdClient.hh \ -src/XrdClient/XrdClientUnsolMsg.hh \ -src/XrdClient/XrdClientAdmin.hh \ -src/XrdClient/XrdClientUrlSet.hh \ -src/XrdClient/XrdClientUrlInfo.hh \ -src/XrdClient/XrdClientEnv.hh \ -src/XrdOuc/XrdOucRash.hh \ -src/XrdOuc/XrdOucStream.hh \ -src/XrdOuc/XrdOuca2x.hh \ -src/XrdOuc/XrdOucTrace.hh \ -src/XrdOuc/XrdOucCRC.hh \ -src/XrdOuc/XrdOucErrInfo.hh \ -src/XrdOuc/XrdOucDLlist.hh \ -src/XrdOuc/XrdOucCache.hh \ -src/XrdOuc/XrdOucTList.hh \ -src/XrdOuc/XrdOucName2Name.hh \ -src/XrdOuc/XrdOucTable.hh \ -src/XrdOuc/XrdOucIOVec.hh \ -src/XrdOuc/XrdOucCallBack.hh \ -src/XrdOuc/XrdOucEnum.hh \ -src/XrdOuc/XrdOucLock.hh \ -src/XrdOuc/XrdOucTokenizer.hh \ -src/XrdOuc/XrdOucEnv.hh \ -src/XrdOuc/XrdOucString.hh \ -src/XrdOuc/XrdOucChain.hh \ -src/XrdOuc/XrdOucUtils.hh \ -src/XrdOuc/XrdOucHash.hh \ -src/XrdOss/XrdOss.hh \ -src/XrdOss/XrdOssStatInfo.hh \ -src/XrdOss/XrdOssDefaultSS.hh \ -src/XrdPosix/XrdPosixXrootd.hh \ -src/XrdPosix/XrdPosixExtern.hh \ -src/XrdPosix/XrdPosixXrootdPath.hh \ -src/XrdPosix/XrdPosixOsDep.hh \ -src/XrdPosix/XrdPosixCallBack.hh \ -src/XrdAcc/XrdAccAuthorize.hh \ -src/XrdAcc/XrdAccPrivs.hh \ -src/XProtocol/XPtypes.hh \ -src/XProtocol/XProtocol.hh \ -src/XrdCms/XrdCmsClient.hh \ -src/XrdCl/XrdClEnv.hh \ -src/XrdCl/XrdClPostMaster.hh \ -src/XrdCl/XrdClFileSystem.hh \ -src/XrdCl/XrdClPostMasterInterfaces.hh \ -src/XrdCl/XrdClBuffer.hh \ -src/XrdCl/XrdClConstants.hh \ -src/XrdCl/XrdClCopyProcess.hh \ -src/XrdCl/XrdClDefaultEnv.hh \ -src/XrdCl/XrdClMessage.hh \ -src/XrdCl/XrdClMonitor.hh \ -src/XrdCl/XrdClStatus.hh \ -src/XrdCl/XrdClTransportManager.hh \ -src/XrdCl/XrdClURL.hh \ -src/XrdCl/XrdClAnyObject.hh \ -src/XrdCl/XrdClXRootDResponses.hh \ -src/XrdCl/XrdClFile.hh \ -src/XrdFileCache/XrdFileCacheDecision.hh -src/XrdFileCache/XrdFileCacheFile.hh -src/XrdFileCache/XrdFileCache.hh -src/XrdFileCache/XrdFileCacheInfo.hh -src/XrdFileCache/XrdFileCacheIOEntireFile.hh -src/XrdFileCache/XrdFileCacheIOFileBlock.hh -src/XrdFileCache/XrdFileCacheIO.hh -src/XrdFileCache/XrdFileCachePrint.hh -src/XrdFileCache/XrdFileCacheStats.hh -src/XrdFileCache/XrdFileCacheTrace.hh - -FILE_PATTERNS = *.hh -RECURSIVE = YES -EXCLUDE = -EXCLUDE_SYMLINKS = NO -EXCLUDE_PATTERNS = -EXAMPLE_PATH = -EXAMPLE_PATTERNS = -EXAMPLE_RECURSIVE = NO -IMAGE_PATH = -INPUT_FILTER = -FILTER_SOURCE_FILES = NO -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- -SOURCE_BROWSER = NO -INLINE_SOURCES = NO -STRIP_CODE_COMMENTS = YES -REFERENCED_BY_RELATION = YES -REFERENCES_RELATION = YES -VERBATIM_HEADERS = YES -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- -ALPHABETICAL_INDEX = NO -COLS_IN_ALPHA_INDEX = 5 -IGNORE_PREFIX = -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- -GENERATE_HTML = YES -HTML_OUTPUT = html -HTML_FILE_EXTENSION = .html -HTML_HEADER = -HTML_FOOTER = -HTML_STYLESHEET = -HTML_ALIGN_MEMBERS = YES -GENERATE_HTMLHELP = NO -CHM_FILE = -HHC_LOCATION = -GENERATE_CHI = NO -BINARY_TOC = NO -TOC_EXPAND = NO -DISABLE_INDEX = NO -ENUM_VALUES_PER_LINE = 4 -GENERATE_TREEVIEW = NO -TREEVIEW_WIDTH = 250 -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- -GENERATE_LATEX = NO -LATEX_OUTPUT = latex -LATEX_CMD_NAME = latex -MAKEINDEX_CMD_NAME = makeindex -COMPACT_LATEX = NO -PAPER_TYPE = a4wide -EXTRA_PACKAGES = -LATEX_HEADER = -PDF_HYPERLINKS = NO -USE_PDFLATEX = NO -LATEX_BATCHMODE = NO -LATEX_HIDE_INDICES = NO -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- -GENERATE_RTF = NO -RTF_OUTPUT = rtf -COMPACT_RTF = NO -RTF_HYPERLINKS = NO -RTF_STYLESHEET_FILE = -RTF_EXTENSIONS_FILE = -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- -GENERATE_MAN = NO -MAN_OUTPUT = man -MAN_EXTENSION = .3 -MAN_LINKS = NO -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- -GENERATE_XML = NO -XML_OUTPUT = xml -XML_SCHEMA = -XML_DTD = -XML_PROGRAMLISTING = YES -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- -GENERATE_AUTOGEN_DEF = NO -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- -GENERATE_PERLMOD = NO -PERLMOD_LATEX = NO -PERLMOD_PRETTY = YES -PERLMOD_MAKEVAR_PREFIX = -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- -ENABLE_PREPROCESSING = YES -MACRO_EXPANSION = NO -EXPAND_ONLY_PREDEF = NO -SEARCH_INCLUDES = YES -INCLUDE_PATH = -INCLUDE_FILE_PATTERNS = -PREDEFINED = -EXPAND_AS_DEFINED = -SKIP_FUNCTION_MACROS = YES -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- -TAGFILES = -GENERATE_TAGFILE = -ALLEXTERNALS = NO -EXTERNAL_GROUPS = YES -PERL_PATH = /usr/bin/perl -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- -CLASS_DIAGRAMS = YES -HIDE_UNDOC_RELATIONS = YES -HAVE_DOT = YES -CLASS_GRAPH = YES -COLLABORATION_GRAPH = YES -UML_LOOK = NO -TEMPLATE_RELATIONS = NO -INCLUDE_GRAPH = YES -INCLUDED_BY_GRAPH = YES -CALL_GRAPH = NO -GRAPHICAL_HIERARCHY = YES -DOT_IMAGE_FORMAT = png -DOT_PATH = -DOTFILE_DIRS = -MAX_DOT_GRAPH_WIDTH = 1024 -MAX_DOT_GRAPH_HEIGHT = 1024 -MAX_DOT_GRAPH_DEPTH = 0 -GENERATE_LEGEND = YES -DOT_CLEANUP = YES -#--------------------------------------------------------------------------- -# Configuration::additions related to the search engine -#--------------------------------------------------------------------------- -SEARCHENGINE = NO diff --git a/src/XrdCeph/LICENSE b/src/XrdCeph/LICENSE deleted file mode 100644 index 8fd5621be2c..00000000000 --- a/src/XrdCeph/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -"Copyright (c) 2005-2012, Board of Trustees of the Leland Stanford, Jr. University.\n" -"Produced under contract DE-AC02-76-SF00515 with the US Department of Energy. \n" -"All rights reserved. The copyright holder's institutional names may not be used to\n" -"endorse or promote products derived from this software without specific prior \n" -"written permission.\n\n" -"This file is part of the XRootD software suite. \n\n" -"XRootD is free software: you can redistribute it and/or modify it under the terms \n" -"of the GNU Lesser General Public License as published by the Free Software \n" -"Foundation, either version 3 of the License, or (at your option) any later version.\n\n" -"XRootD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n" -"without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR \n" -"PURPOSE. See the GNU Lesser General Public License for more details. \nn" -"You should have received a copy of the GNU Lesser General Public License along \n" -"with XRootD in a file called COPYING.LESSER (LGPL license) and file COPYING (GPL \n" -"license). If not, see .\n\n" -"Prior to September 2nd, 2012 the XRootD software suite was licensed under a\n" -"modified BSD license (see file COPYING.BSD). This applies to all code that\n" -"was in the XRootD git repository prior to that date.\n" diff --git a/src/XrdCeph/README b/src/XrdCeph/README deleted file mode 100644 index e2bf17c3df6..00000000000 --- a/src/XrdCeph/README +++ /dev/null @@ -1,54 +0,0 @@ - --------------------------------------------------------------------------------- - _ _ ______ _____ - \ \ / (_____ \ _ (____ \ - \ \/ / _____) ) ___ ___ | |_ _ \ \ - ) ( (_____ ( / _ \ / _ \| _)| | | | - / /\ \ | | |_| | |_| | |__| |__/ / - /_/ \_\ |_|\___/ \___/ \___)_____/ - --------------------------------------------------------------------------------- - -0. xrootd-ceph is a OSS layer XRootD plug-in for interfacing with Ceph storage - platform. The plug-in has to be build against respective Ceph version, the - repository can be found at: - - https://download.ceph.com/rpm-{ceph-release}/{distro}/$basearch - -1. S U P P O R T E D O P E R A T I N G S Y S T E M S - - XRootD is supported on the following platforms: - - * RedHat Enterprise Linux 7 and derivatives (Scientific Linux) - compiled with gcc - -2. B U I L D I N S T R U C T I O N S - -2.1 Build system - - xrootd-ceph uses CMake to handle the build process. Please use CMake version 3 or greater (e.g. cmake3). - -2.2 Build steps - - * Create an empty build directory: - - mkdir build - cd build - - * Ensure that the correct plugin version number is set in cmake/XRootDDefaults.cmake: - - if( NOT XRDCEPH_SUBMODULE ) - define_default( PLUGIN_VERSION 5 ) - endif() - - * Generate the build system files using cmake, ie: - - cmake /path/to/the/xrootd-ceph/source -DCMAKE_INSTALL_PREFIX=/opt/xrootd - - * Build the source: - - make - - * Install the shared libraries: - - make install diff --git a/src/XrdCeph/VERSION_INFO b/src/XrdCeph/VERSION_INFO deleted file mode 100644 index 5ac45999939..00000000000 --- a/src/XrdCeph/VERSION_INFO +++ /dev/null @@ -1 +0,0 @@ -$Format:RefNames: %d%nShortHash: %h%nDate: %ai%n$ \ No newline at end of file diff --git a/src/XrdCeph/src/XrdCeph/XrdCephOss.cc b/src/XrdCeph/XrdCephOss.cc similarity index 99% rename from src/XrdCeph/src/XrdCeph/XrdCephOss.cc rename to src/XrdCeph/XrdCephOss.cc index 5a9ada4f51b..dc707a02771 100644 --- a/src/XrdCeph/src/XrdCeph/XrdCephOss.cc +++ b/src/XrdCeph/XrdCephOss.cc @@ -26,21 +26,18 @@ #include #include +#include "XrdVersion.hh" +#include "XrdCeph/XrdCephOss.hh" +#include "XrdCeph/XrdCephOssDir.hh" +#include "XrdCeph/XrdCephOssFile.hh" #include "XrdCeph/XrdCephPosix.hh" + #include "XrdOuc/XrdOucEnv.hh" #include "XrdSys/XrdSysError.hh" #include "XrdOuc/XrdOucTrace.hh" #include "XrdOuc/XrdOucStream.hh" #include "XrdOuc/XrdOucName2Name.hh" -#ifdef XRDCEPH_SUBMODULE #include "XrdOuc/XrdOucN2NLoader.hh" -#else -#include "private/XrdOuc/XrdOucN2NLoader.hh" -#endif -#include "XrdVersion.hh" -#include "XrdCeph/XrdCephOss.hh" -#include "XrdCeph/XrdCephOssDir.hh" -#include "XrdCeph/XrdCephOssFile.hh" XrdVERSIONINFO(XrdOssGetStorageSystem, XrdCephOss); diff --git a/src/XrdCeph/src/XrdCeph/XrdCephOss.hh b/src/XrdCeph/XrdCephOss.hh similarity index 99% rename from src/XrdCeph/src/XrdCeph/XrdCephOss.hh rename to src/XrdCeph/XrdCephOss.hh index 838030dc3db..80cb1a25646 100644 --- a/src/XrdCeph/src/XrdCeph/XrdCephOss.hh +++ b/src/XrdCeph/XrdCephOss.hh @@ -26,7 +26,9 @@ #define __CEPH_OSS_HH__ #include + #include +#include //------------------------------------------------------------------------------ //! This class implements XrdOss interface for usage with a CEPH storage. diff --git a/src/XrdCeph/src/XrdCeph/XrdCephOssDir.cc b/src/XrdCeph/XrdCephOssDir.cc similarity index 100% rename from src/XrdCeph/src/XrdCeph/XrdCephOssDir.cc rename to src/XrdCeph/XrdCephOssDir.cc diff --git a/src/XrdCeph/src/XrdCeph/XrdCephOssDir.hh b/src/XrdCeph/XrdCephOssDir.hh similarity index 100% rename from src/XrdCeph/src/XrdCeph/XrdCephOssDir.hh rename to src/XrdCeph/XrdCephOssDir.hh diff --git a/src/XrdCeph/src/XrdCeph/XrdCephOssFile.cc b/src/XrdCeph/XrdCephOssFile.cc similarity index 100% rename from src/XrdCeph/src/XrdCeph/XrdCephOssFile.cc rename to src/XrdCeph/XrdCephOssFile.cc diff --git a/src/XrdCeph/src/XrdCeph/XrdCephOssFile.hh b/src/XrdCeph/XrdCephOssFile.hh similarity index 100% rename from src/XrdCeph/src/XrdCeph/XrdCephOssFile.hh rename to src/XrdCeph/XrdCephOssFile.hh diff --git a/src/XrdCeph/src/XrdCeph/XrdCephPosix.cc b/src/XrdCeph/XrdCephPosix.cc similarity index 100% rename from src/XrdCeph/src/XrdCeph/XrdCephPosix.cc rename to src/XrdCeph/XrdCephPosix.cc diff --git a/src/XrdCeph/src/XrdCeph/XrdCephPosix.hh b/src/XrdCeph/XrdCephPosix.hh similarity index 100% rename from src/XrdCeph/src/XrdCeph/XrdCephPosix.hh rename to src/XrdCeph/XrdCephPosix.hh diff --git a/src/XrdCeph/src/XrdCeph/XrdCephXAttr.cc b/src/XrdCeph/XrdCephXAttr.cc similarity index 100% rename from src/XrdCeph/src/XrdCeph/XrdCephXAttr.cc rename to src/XrdCeph/XrdCephXAttr.cc diff --git a/src/XrdCeph/src/XrdCeph/XrdCephXAttr.hh b/src/XrdCeph/XrdCephXAttr.hh similarity index 100% rename from src/XrdCeph/src/XrdCeph/XrdCephXAttr.hh rename to src/XrdCeph/XrdCephXAttr.hh diff --git a/src/XrdCeph/cmake/FindCppUnit.cmake b/src/XrdCeph/cmake/FindCppUnit.cmake deleted file mode 100644 index cc6e7400191..00000000000 --- a/src/XrdCeph/cmake/FindCppUnit.cmake +++ /dev/null @@ -1,30 +0,0 @@ -# Try to find CPPUnit -# Once done, this will define -# -# CPPUNIT_FOUND - system has cppunit -# CPPUNIT_INCLUDE_DIRS - the cppunit include directories -# CPPUNIT_LIBRARIES - cppunit libraries directories - -find_path( CPPUNIT_INCLUDE_DIRS cppunit/ui/text/TestRunner.h - HINTS - ${CPPUNIT_DIR} - $ENV{CPPUNIT_DIR} - /usr - /opt - PATH_SUFFIXES include -) - -find_library( CPPUNIT_LIBRARIES cppunit - HINTS - ${CPPUNIT_DIR} - $ENV{CPPUNIT_DIR} - /usr - /opt - PATH_SUFFIXES lib -) - -set(CPPUNIT_INCLUDE_DIRS ${CPPUNIT_INCLUDE_DIR}) -set(CPPUNIT_LIBRARIES ${CPPUNIT_LIBRARY}) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(CppUnit DEFAULT_MSG CPPUNIT_INCLUDE_DIRS CPPUNIT_LIBRARIES) diff --git a/src/XrdCeph/cmake/FindXRootD.cmake b/src/XrdCeph/cmake/FindXRootD.cmake deleted file mode 100644 index a3596ebc338..00000000000 --- a/src/XrdCeph/cmake/FindXRootD.cmake +++ /dev/null @@ -1,35 +0,0 @@ -# Try to find XRootD -# Once done, this will define -# -# XROOTD_FOUND - system has xrootd -# XROOTD_INCLUDE_DIRS - the xrootd include directories -# XROOTD_LIBRARIES - xrootd libraries directories - -if( XRDCEPH_SUBMODULE ) - set( XROOTD_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src ) - set( XROOTD_LIBRARIES XrdUtils ) -else() - find_path( XROOTD_INCLUDE_DIRS XrdSfs/XrdSfsAio.hh - HINTS - ${XROOTD_DIR} - $ENV{XROOTD_DIR} - /usr - /opt - PATH_SUFFIXES include/xrootd - ) - - find_library( XROOTD_LIBRARIES XrdUtils - HINTS - ${XROOTD_DIR} - $ENV{XROOTD_DIR} - /usr - /opt - PATH_SUFFIXES lib - ) -endif() - -set(XROOTD_INCLUDE_DIR ${XROOTD_INCLUDE_DIRS}) -set(XROOTD_LIBRARY ${XROOTD_LIBRARIES}) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(XRootD DEFAULT_MSG XROOTD_INCLUDE_DIRS XROOTD_LIBRARIES) diff --git a/src/XrdCeph/cmake/GNUInstallDirs.cmake b/src/XrdCeph/cmake/GNUInstallDirs.cmake deleted file mode 100644 index a114dcb2e18..00000000000 --- a/src/XrdCeph/cmake/GNUInstallDirs.cmake +++ /dev/null @@ -1,182 +0,0 @@ -# - Define GNU standard installation directories -# Provides install directory variables as defined for GNU software: -# http://www.gnu.org/prep/standards/html_node/Directory-Variables.html -# Inclusion of this module defines the following variables: -# CMAKE_INSTALL_ - destination for files of a given type -# CMAKE_INSTALL_FULL_ - corresponding absolute path -# where is one of: -# BINDIR - user executables (bin) -# SBINDIR - system admin executables (sbin) -# LIBEXECDIR - program executables (libexec) -# SYSCONFDIR - read-only single-machine data (etc) -# SHAREDSTATEDIR - modifiable architecture-independent data (com) -# LOCALSTATEDIR - modifiable single-machine data (var) -# LIBDIR - object code libraries (lib or lib64) -# INCLUDEDIR - C header files (include) -# OLDINCLUDEDIR - C header files for non-gcc (/usr/include) -# DATAROOTDIR - read-only architecture-independent data root (share) -# DATADIR - read-only architecture-independent data (DATAROOTDIR) -# INFODIR - info documentation (DATAROOTDIR/info) -# LOCALEDIR - locale-dependent data (DATAROOTDIR/locale) -# MANDIR - man documentation (DATAROOTDIR/man) -# DOCDIR - documentation root (DATAROOTDIR/doc/PROJECT_NAME) -# Each CMAKE_INSTALL_ value may be passed to the DESTINATION options of -# install() commands for the corresponding file type. If the includer does -# not define a value the above-shown default will be used and the value will -# appear in the cache for editing by the user. -# Each CMAKE_INSTALL_FULL_ value contains an absolute path constructed -# from the corresponding destination by prepending (if necessary) the value -# of CMAKE_INSTALL_PREFIX. - -#============================================================================= -# Copyright 2011 Nikita Krupen'ko -# Copyright 2011 Kitware, Inc. -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distribute this file outside of CMake, substitute the full -# License text for the above reference.) - -# Installation directories -# -if(NOT DEFINED CMAKE_INSTALL_BINDIR) - set(CMAKE_INSTALL_BINDIR "bin" CACHE PATH "user executables (bin)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_SBINDIR) - set(CMAKE_INSTALL_SBINDIR "sbin" CACHE PATH "system admin executables (sbin)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_LIBEXECDIR) - set(CMAKE_INSTALL_LIBEXECDIR "libexec" CACHE PATH "program executables (libexec)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_SYSCONFDIR) - set(CMAKE_INSTALL_SYSCONFDIR "etc" CACHE PATH "read-only single-machine data (etc)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_SHAREDSTATEDIR) - set(CMAKE_INSTALL_SHAREDSTATEDIR "com" CACHE PATH "modifiable architecture-independent data (com)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_LOCALSTATEDIR) - set(CMAKE_INSTALL_LOCALSTATEDIR "var" CACHE PATH "modifiable single-machine data (var)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_LIBDIR) - set(_LIBDIR_DEFAULT "lib") - # Override this default 'lib' with 'lib64' iff: - # - we are on Linux system but NOT cross-compiling - # - we are NOT on debian - # - we are on a 64 bits system - # reason is: amd64 ABI: http://www.x86-64.org/documentation/abi.pdf - # Note that the future of multi-arch handling may be even - # more complicated than that: http://wiki.debian.org/Multiarch - if(CMAKE_SYSTEM_NAME MATCHES "Linux" - AND NOT CMAKE_CROSSCOMPILING - AND NOT EXISTS "/etc/debian_version") - if(NOT DEFINED CMAKE_SIZEOF_VOID_P) - message(AUTHOR_WARNING - "Unable to determine default CMAKE_INSTALL_LIBDIR directory because no target architecture is known. " - "Please enable at least one language before including GNUInstallDirs.") - else() - if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") - set(_LIBDIR_DEFAULT "lib64") - endif() - endif() - endif() - set(CMAKE_INSTALL_LIBDIR "${_LIBDIR_DEFAULT}" CACHE PATH "object code libraries (${_LIBDIR_DEFAULT})") -endif() - -if(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR) - set(CMAKE_INSTALL_INCLUDEDIR "include" CACHE PATH "C header files (include)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_OLDINCLUDEDIR) - set(CMAKE_INSTALL_OLDINCLUDEDIR "/usr/include" CACHE PATH "C header files for non-gcc (/usr/include)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_DATAROOTDIR) - set(CMAKE_INSTALL_DATAROOTDIR "share" CACHE PATH "read-only architecture-independent data root (share)") -endif() - -#----------------------------------------------------------------------------- -# Values whose defaults are relative to DATAROOTDIR. Store empty values in -# the cache and store the defaults in local variables if the cache values are -# not set explicitly. This auto-updates the defaults as DATAROOTDIR changes. - -if(NOT CMAKE_INSTALL_DATADIR) - set(CMAKE_INSTALL_DATADIR "" CACHE PATH "read-only architecture-independent data (DATAROOTDIR)") - set(CMAKE_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}") -endif() - -if(NOT CMAKE_INSTALL_INFODIR) - set(CMAKE_INSTALL_INFODIR "" CACHE PATH "info documentation (DATAROOTDIR/info)") - set(CMAKE_INSTALL_INFODIR "${CMAKE_INSTALL_DATAROOTDIR}/info") -endif() - -if(NOT CMAKE_INSTALL_LOCALEDIR) - set(CMAKE_INSTALL_LOCALEDIR "" CACHE PATH "locale-dependent data (DATAROOTDIR/locale)") - set(CMAKE_INSTALL_LOCALEDIR "${CMAKE_INSTALL_DATAROOTDIR}/locale") -endif() - -if(NOT CMAKE_INSTALL_MANDIR) - set(CMAKE_INSTALL_MANDIR "" CACHE PATH "man documentation (DATAROOTDIR/man)") - set(CMAKE_INSTALL_MANDIR "${CMAKE_INSTALL_DATAROOTDIR}/man") -endif() - -if(NOT CMAKE_INSTALL_DOCDIR) - set(CMAKE_INSTALL_DOCDIR "" CACHE PATH "documentation root (DATAROOTDIR/doc/PROJECT_NAME)") - set(CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME}") -endif() - -#----------------------------------------------------------------------------- - -mark_as_advanced( - CMAKE_INSTALL_BINDIR - CMAKE_INSTALL_SBINDIR - CMAKE_INSTALL_LIBEXECDIR - CMAKE_INSTALL_SYSCONFDIR - CMAKE_INSTALL_SHAREDSTATEDIR - CMAKE_INSTALL_LOCALSTATEDIR - CMAKE_INSTALL_LIBDIR - CMAKE_INSTALL_INCLUDEDIR - CMAKE_INSTALL_OLDINCLUDEDIR - CMAKE_INSTALL_DATAROOTDIR - CMAKE_INSTALL_DATADIR - CMAKE_INSTALL_INFODIR - CMAKE_INSTALL_LOCALEDIR - CMAKE_INSTALL_MANDIR - CMAKE_INSTALL_DOCDIR - ) - -# Result directories -# -foreach(dir - BINDIR - SBINDIR - LIBEXECDIR - SYSCONFDIR - SHAREDSTATEDIR - LOCALSTATEDIR - LIBDIR - INCLUDEDIR - OLDINCLUDEDIR - DATAROOTDIR - DATADIR - INFODIR - LOCALEDIR - MANDIR - DOCDIR - ) - if(NOT IS_ABSOLUTE ${CMAKE_INSTALL_${dir}}) - set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_${dir}}") - else() - set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_${dir}}") - endif() -endforeach() diff --git a/src/XrdCeph/cmake/XRootDDefaults.cmake b/src/XrdCeph/cmake/XRootDDefaults.cmake deleted file mode 100644 index 15416a3046d..00000000000 --- a/src/XrdCeph/cmake/XRootDDefaults.cmake +++ /dev/null @@ -1,17 +0,0 @@ -#------------------------------------------------------------------------------- -# Define the default build parameters -#------------------------------------------------------------------------------- -if( "${CMAKE_BUILD_TYPE}" STREQUAL "" ) - if( Solaris AND NOT SUNCC_CAN_DO_OPTS ) - set( CMAKE_BUILD_TYPE Debug ) - else() - set( CMAKE_BUILD_TYPE RelWithDebInfo ) - endif() -endif() - -if( NOT XRDCEPH_SUBMODULE ) - define_default( PLUGIN_VERSION 5 ) -endif() - -define_default( ENABLE_TESTS FALSE ) -define_default( ENABLE_CEPH TRUE ) diff --git a/src/XrdCeph/cmake/XRootDFindLibs.cmake b/src/XrdCeph/cmake/XRootDFindLibs.cmake deleted file mode 100644 index 78570502ca1..00000000000 --- a/src/XrdCeph/cmake/XRootDFindLibs.cmake +++ /dev/null @@ -1,16 +0,0 @@ -#------------------------------------------------------------------------------- -# Find the required libraries -#------------------------------------------------------------------------------- - -find_package( XRootD REQUIRED ) - -find_package( ceph REQUIRED ) - -if( ENABLE_TESTS ) - find_package( CppUnit ) - if( CPPUNIT_FOUND ) - set( BUILD_TESTS TRUE ) - else() - set( BUILD_TESTS FALSE ) - endif() -endif() diff --git a/src/XrdCeph/cmake/XRootDOSDefs.cmake b/src/XrdCeph/cmake/XRootDOSDefs.cmake deleted file mode 100644 index eadc24952b7..00000000000 --- a/src/XrdCeph/cmake/XRootDOSDefs.cmake +++ /dev/null @@ -1,44 +0,0 @@ -#------------------------------------------------------------------------------- -# Define the OS variables -#------------------------------------------------------------------------------- - -include( CheckCXXSourceRuns ) - -add_definitions( -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 ) -set( LIBRARY_PATH_PREFIX "lib" ) - -#------------------------------------------------------------------------------- -# GCC -#------------------------------------------------------------------------------- -if( CMAKE_COMPILER_IS_GNUCXX ) - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11" ) - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror" ) - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter" ) - # gcc 4.1 is retarded - execute_process( COMMAND ${CMAKE_C_COMPILER} -dumpversion - OUTPUT_VARIABLE GCC_VERSION ) - if( (GCC_VERSION VERSION_GREATER 4.1 OR GCC_VERSION VERSION_EQUAL 4.1) - AND GCC_VERSION VERSION_LESS 4.2 ) - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-strict-aliasing" ) - endif() - - # for 4.9.3 or greater the 'omit-frame-pointer' - # interfears with custom semaphore implementation - if( (GCC_VERSION VERSION_GREATER 4.9.2) AND (USE_LIBC_SEMAPHORE EQUAL 0) ) - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer" ) - endif() - - # gcc 6.0 is more pedantic - if( GCC_VERSION VERSION_GREATER 6.0 OR GCC_VERSION VERSION_EQUAL 6.0 ) - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=misleading-indentation" ) - endif() -endif() - -#------------------------------------------------------------------------------- -# Linux -#------------------------------------------------------------------------------- -set( Linux TRUE ) -include( GNUInstallDirs ) -add_definitions( -D__linux__=1 ) -set( EXTRA_LIBS rt ) - diff --git a/src/XrdCeph/cmake/XRootDSummary.cmake b/src/XrdCeph/cmake/XRootDSummary.cmake deleted file mode 100644 index d8294d37c2f..00000000000 --- a/src/XrdCeph/cmake/XRootDSummary.cmake +++ /dev/null @@ -1,21 +0,0 @@ -#------------------------------------------------------------------------------- -# Print the configuration summary -#------------------------------------------------------------------------------- -set( TRUE_VAR TRUE ) -component_status( CEPH TRUE_VAR CEPH_FOUND ) -component_status( XROOTD TRUE_VAR XROOTD_FOUND ) -component_status( TESTS BUILD_TESTS CPPUNIT_FOUND ) - -if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) -message( STATUS "----------------------------------------" ) -message( STATUS "Installation path: " ${CMAKE_INSTALL_PREFIX} ) -message( STATUS "C Compiler: " ${CMAKE_C_COMPILER} ) -message( STATUS "C++ Compiler: " ${CMAKE_CXX_COMPILER} ) -message( STATUS "Build type: " ${CMAKE_BUILD_TYPE} ) -message( STATUS "Plug-in version: " ${PLUGIN_VERSION} ) -message( STATUS "" ) -message( STATUS "CEPH: " ${STATUS_CEPH} ) -message( STATUS "XRootD: " ${STATUS_XROOTD} ) -message( STATUS "Tests: " ${STATUS_TESTS} ) -message( STATUS "----------------------------------------" ) -endif() diff --git a/src/XrdCeph/cmake/XRootDUtils.cmake b/src/XrdCeph/cmake/XRootDUtils.cmake deleted file mode 100644 index 6fbdf0fc9e7..00000000000 --- a/src/XrdCeph/cmake/XRootDUtils.cmake +++ /dev/null @@ -1,39 +0,0 @@ - -#------------------------------------------------------------------------------- -# Add a compiler define flag if a variable is defined -#------------------------------------------------------------------------------- -macro( define_default variable value ) - if( NOT DEFINED ${variable} ) - set( ${variable} ${value} ) - endif() -endmacro() - -macro( component_status name flag found ) - if( ${flag} AND ${found} ) - set( STATUS_${name} "yes" ) - elseif( ${flag} AND NOT ${found} ) - set( STATUS_${name} "libs not found" ) - else() - set( STATUS_${name} "disabled" ) - endif() -endmacro() - -#------------------------------------------------------------------------------- -# Detect in source builds -#------------------------------------------------------------------------------- -function( CheckBuildDirectory ) - - # Get Real Paths of the source and binary directories - get_filename_component( srcdir "${CMAKE_SOURCE_DIR}" REALPATH ) - get_filename_component( bindir "${CMAKE_BINARY_DIR}" REALPATH ) - - # Check for in-source builds - if( ${srcdir} STREQUAL ${bindir} ) - message( FATAL_ERROR "XRootD cannot be built in-source! " - "Please run cmake outside the " - "source directory and be sure to remove " - "CMakeCache.txt or CMakeFiles if they " - "exist in the source directory." ) - endif() - -endfunction() diff --git a/src/XrdCeph/docs/PreReleaseNotes.txt b/src/XrdCeph/docs/PreReleaseNotes.txt deleted file mode 100644 index a414e1aa765..00000000000 --- a/src/XrdCeph/docs/PreReleaseNotes.txt +++ /dev/null @@ -1,8 +0,0 @@ -====== -XRootD -====== - -Prerelease Notes -================ - - diff --git a/src/XrdCeph/docs/ReleaseNotes.txt b/src/XrdCeph/docs/ReleaseNotes.txt deleted file mode 100644 index c8a8ceb19dc..00000000000 --- a/src/XrdCeph/docs/ReleaseNotes.txt +++ /dev/null @@ -1,1692 +0,0 @@ -====== -XRootD -====== - -Release Notes -============= - - -------------- -Version 4.8.0 -------------- - -+ **New Features** - * **[XrdCl]** Local redirection and local file support. - * **[XrdCl]** merge xrdfs ls results if not unique, closes #541. - * **[XrdCl]** Provide client specific CGI info. - * **[XrdCl]** File::WriteV implementation, closes #388. - * **[XrdHttp]** Pass the HTTP verb to the external handler for path - matching. - * **[XrdHttp]** Allow one to access the XrdSecEntity object associated - with a request. - * **[XrdHttp]** Allow filtering based on HTTP verb in MatchesPath. - * **[XrdHttp]** Allow overwrites to be done on PUT. - * **[XrdHttp]** Allow multiple external handlers to be loaded by XrdHttp. - -+ **Major bug fixes** - * **[Server]** Correctly handle monEnt on file close to avoid SEGV. - Fixes #618. - * **[Server]** Poperly handle file descriptors up to 65535. - Fixes #607. - * **[Server]** Fix handling of >65K attached files (active links). - Fixes #623. - * **[Server]** Make sure doPost does not become <0 (regression introduced - in 4.7.1). - * **[Proxy]** Avoid SEGV when localroot specified w/o remote root. - Fixes #627. - * **[XrdCl]** Connection Window should be applied per IP address. - Fixes #625. - * **[XrdCl]** Write request and raw data with single writev, fixes #609. - * **[XrdHttp]** Allow XrdSfsGetDefaultFileSystem to be called multiple - times. - * **[XrdHttp]** Correct external handling logic. - * **[XrdSecgsi]** Use stack for proper cleaning of invalidated CRLs and CAs. - -+ **Minor bug fixes** - * **[Server]** Print error msg and close socket when a FD cannot. - be handled. - * **[Server]** Close additional loophole for fstream disconnect. - * **[Server]** Always unhook the statistcs object from xfr monitoring - if hooked. - * **[Server]** Ruggedize TPC to be less sensitive to protocol violations. - * **[Server]** Correct tpc directive scanning and make it more obvious. - Fixes #604. - * **[Server]** Enable url rewrites. Eliminates GSI roadblock. - * **[Server]** Do not reference a deleted object. - * **[XrdSsi]** Make sure to finalyze all requests upon disc, fixes #616. - * **[XrdHttp]** Handle properly http.secretkey. - * **[XrdCl]** various memory releated fixes. - * **[XrdPy]** Translate binary buffers into bytes objects, closes #632 - -+ **Miscellaneous** - * **[RPM]** Add python3 sub package. - * **[RPM]** Rename python sub-package, closes #614. - * **[Py]** Facilitate building python bindings with wheel. - -------------- -Version 4.7.1 -------------- - -+ **Major bug fixes** - * **[XrdSecgsi]** Fix segv in cache checking, fixes #595 - * **[XrdHttp]** Fix for the persistent connection issue. - * **[XrdHttp]** Fix FD leak when a socket error is encountered. - * **[XrdSsi]** Avoid race condition when response is posted. - * **[XrdSsi]** Avoid state conflict when request is being processed and - client asks for response. - * **[XrdCl]** Prevent segv in case user has no name. - * **[Server]** Close link on enable errors to prevent socket leaks. - -+ **Minor bug fixes** - * **[XrdLink]** Increment the IOSemaphore once for each waiting thread. - * **[XrdHttp]** Make sure that the preexisting url tokens are properly - quoted when generating a redirection. - * **[XrdCl]** Fix invalid memory reads/writes when RAII finalizes mutex - after the object has been deleted. - -+ **Miscellaneous** - * **[XrdCl]** Log last error in case redirect limit has been reached. - * **[XrdCl]** Add option to read credentials under different fsuid/fsgid. - * **[XrdCl]** Accept empty login response for protocol <= 2.8.9 - (This is only to ensure compatibility with dCache, which - due to its inaccurate implementation of XRoot protocol in - some cases returns an empty login response for protocol - version <= 2.8.9.) - * **[XrdCl]** Add envar to config Nagle algorithm. - * **[XrdSsi]** Reinitializ response object after Finished() so it can - reused. -* **[XrdHttp]** Header2cgi directive. - -------------- -Version 4.7.0 -------------- - -+ **New Features** - * **[Proxy]** Make cache I/O synchronization tunable. - * **[Proxy]** Allow caching of S3-style objects. - * **[Proxy/Posix]** Allow Name2Name to populate cache using the LFN. - * **[Posix]** Enable LITE feature in Posix preload library. - * **[Posix]** Implement serverless file caching (disk or memory). - * **[Server]** Allow storing S3-style objects in a file system. - * **[Server]** Add xrootd.fsoverload directive to handle filesystem overloads. - * **[Server]** Allow port to be specified for a supervisor. - * **[Server]** Add org and role types to AuthDB authorization. - * **[Server]** Allow definition and test of compound authorization identifiers. - * **[Server/Packaging]** Handle systemd socket inheritance. - * **[XrdApps]** Add XrdClProxyPlugin implementation. - * **[XrdCl]** Extreme copy implementation. - * **[XrdCl]** Delegate all callbacks to the thread-pool. - * **[XrdCl]** xrdfs: add recursive list, closes #421. - * **[XrdCeph]** Added support for namelib in ceph plugin . - * **[XrdFfs]** Implement xrootdfs_create. - * **[Python]** Python 3 support in C / Python interface. - * **[XrdHttp]** Make XrdHTTP able to forward HTTP requests to an external, - optional plugin (conceptually similar to CGI). - * **[Server]** XrdSsi V2 (scalable service interface) implementation. - -+ **Major bug fixes** - * **[XrdCl]** Avoid deadlock between FSH deletion and Tick() timeout. - * **[XrdCl]** Process virtual redierections in the threadpool. - * **[Xrd] Fix handling of sendfile offset argument. - -+ **Minor bug fixes** - * **[Server]** Make file locking independent of FS plugin. Fixes #533 - * **[Server]** Correct debug message interval for free space report. - * **[XrdCeph]** Fixed internal (f)stat so that it sets S_IFREG in returned mode. - * **[XrdCeph]** properly return ENOENT when file does not exist in open for read. - * **[XrdCeph]** Fixed configuration of the XrdCephOss module. - * **[XrdCeph]** Fixed some resource leak when Posix_Open fails. - * **[XrdFfs]** Remove default fuse argument "allow_other" as it is impossible - to unset. - * **[XrdFfs]** Check file descriptor before using it in xrootdfs wcache. - * **[XrdFfs]** Add more error checks when creating write cache. - * **[XrdFfs]** Avoid using literal 1024, replace with MAXROOTURLLEN. - * **[XrdFfs]** Control allow_other by env XROOTD_NOALLOWOTHER. - * **[XrdFfs]** Rewrite xrootdfs_mknod, extract low-level function. - * **[XrdCl]** Check login resp size, fixes #530 - * **[XrdCl]** Avoid FileStateHandler deadlock while forking. - * **[XrdCl]** Handle failed stateful operations without XrdCl::File lock - being locked. - * **[XrdPosix]** Use strncpy when copying checksum. - * **[RPM]** Fix init script bad exit code, fixes #536 - * **[XrdBuffer]** Decrement total buffer count when freeing buffers. - -+ **Miscellaneous** - * **[Server]** Re-enable the oss.fdlimit directive to allow POSIX preload+xrootd. - * **[Server]** Avoid thread pile-up durin slow close operations. - * **[Proxy]** Simplify delayed destruction on wait vs post. - * **[Posix]** Convert to using universal tracing facility. - * **[CI]** Add Travis CI configuration. - * **[CI]** Add .gitlab-ci.yml for gitlab CI. - * **[Packaging]** Add a sample XrdHttp config file. - * **[Packaging]** Make RPM version configurable by the user. - * **[Packaging]** Debian packaging. - * **[RPM/CMake]** Enable c++0x/c++11 by default. - * **[Crypto] Remove unused crypto code. - * **[XrdFileCache]** Add configuration parameter for flush frequency. - * **[XrdFileCache]** Alter ram limits and blocks size parameter if caching - is on the client side. - * **[XrdSut]** New XrdSutCache based on XrdOucHash. - * **[XrdSecgsi]** do not delete explicitely the CRL in Delete. - * **[XrdSut/Crypto]** Secgsi improvements: new version of XrdSutCache, - lightweith locking (PR #539). - -------------- -Version 4.6.1 -------------- - -+ **Major bug fixes** - - * **[Server/Proxy]** Avoid SEGV when close(), closedir() returns an error. - * **[cmsd]** Fix feature interaction causing improper file existence to be sent. - * **[XrdCrypto/XrdSecgsi]** Make sure the CRL is loaded for the right CA. - * **[XrdCrypto]** Support for OpenSSL 1.1 - * **[XrdSecgsi]** do not build/package libXrdSecgsiGMAPLDAP-4.so. - * **[XrdSecgsi]** Improve detection of errors when loading CRL. - * **[XrdSecgsi]** Fix for valid legacy proxy detection (PR #469) - * **[XrdSecgsi]** Absent CRLs not an error (#465) - * **[XrdSecgsi]** Fix for CA chain verification segfault (issue #463) - * **[XrdSecgsi]** Two memory leaks (PR #503) - * **[XrdCl]** Make sure there is no request/response mismatch, when - the retry logics tries to recover from an error. - * **[XrdCl/Server]** Be case insensitive when it comes to checksum names. - * **[XrdCeph]** Fix ability to read back a file written with O_RDWR flags. - * **[XrdCeph]** Disable logging of every read and write operation. A proper - debug-level logging would be needed instead. - * **[XrdCeph]** Added statistics about read/write operations in the - close log. - -+ **Minor bug fixes** - * **[XrdHttp]** Make the XrdHttpSecXtractor API backwards compatible. - * **[XrdFileCache]** Make caching proxy configuration backwards - compatible. - * **[XrdFileCache]** Fix cache v1 to cache v2 bridge after introducing - cache v2. - * **[XrdSec]** Use CommonCrypto header instead of openssl for SHA on OSX. - * **[XrdSeckrb5]** Fix memory leaks in client context and cache. - * **[Server/Logrotate]** Make sure XRootD logrotate does not interfire with - system logrotate, fixes #490 - * ** [Server]** Avoid std::ABORT should a naked logfile path be specified. - * **[XrdCl]** Make sure ForkHandler doesn't segv if PostMaster is null, - fixes #489 - * **[Packaging]** Set the working dir to /var/spool/xrootd on CC7, - fixes #365 - * **[Packaging]** On platforms where systemd is available, manage files in - /var/run with tmpfiles.d, fixes #485 - -+ **Miscellaneous** - * **[XrdPosix]** Add new minpages option to pss.cache to support large pages. - * **[XrdPosix]** Make XrdPosix.hh a public header; closes #479 - * **[XrdApps]** Remove XrdClient dependency from xrdadler32. - * **[Server]** Add XrdCksAssist functions to help handle XRootD checksums. - * **[Server/Proxy]** Move disk sync operations out of IO::ioActive() call. - * **[Server/Proxy]** Change severity IO::initLocalStat() log message. - * **[XrdFileCache]** Ease development of decision plugins. -* **[XrdFileCache]** Use ref-counts on File objects. - -------------- -Version 4.6.0 -------------- - -+ **New Features** - * **[XrdCms]** Add non-blocking sends to avoid slow links. - * **[XrdFileCache]** File caching proxy V2 (and new pss async interface). - -+ **Major bug fixes** - * **[XrdCeph]** Account for return Ceph xattr return codes. - * **[XrdCeph]** Fixed initialization of Ceph clusters when stripers are not used. - * **[XrdCrypto]** Improved determination of X509 certificate type, - including proxy version - * **[XrdHttp]** Fix memory leak in Bridge protocol (affects HTTP). - * **[XrdSecgsi]** Several improvements in the way CRLs are checked and reloaded. - * **[XrdCl]** Protect against spurious wakeups in SyncResponseHandler. - * **[XrdCl]** On read-timeout, if the stream is broken, make sure the request and - its handler are not double deleted. - -+ **Minor bug fixes** - * **[XrdCl]** Check if the file was correctly closed upon ZipArchiveReader destruction. - * **[Server]** Add limits for prepare requests. - * **[Server]** Delete buffers when the buffer manager is deleted. Fixes #414 - * **[Server]** Do not double count overlapping spaces. Fixes #425 - * **[XrdHttp]** Allow unauthenticated https clients. - * **[XrdHttp]** Make Xrdhttp secure by default (rejecting proxy cert in the absence - of a proper SecXtractor plugin) - -+ **Miscellaneous** - * **[XrdSecgsi]** Re-activate xrdgsitest - * **[RPM]** Include xrdgsitest in xrootd-client-devel package. - * **[XrdFileCache]** Add example of filecache configuration. - -------------- -Version 4.5.0 -------------- - -+ **New Features** - * **[XrdCms]** Allow specifying a different timeout for null cached entries; fixes #413 - * **[XProtocol/XrdSec/Server/XrdCl]** Implement request signing. - * **[XrdCl]** Add ZIP extracting capability to xrdcp. - * **[XrdCl]** Include the release number in client Login request cgi. - * **[XrdCl]** Add support for spaces in file names for mv operation. - -+ **Major bug fixes** - * **[XrdCrypto/Secgsi]** Fix XrdCryptosslMsgDigest::Init ; set 'sha256' as - default algorithm. - * **[XrdCl]** Use posix semaphores for fedora >= 22. Disable - omit-frame-ponter for gcc >= 4.9.3 if custom semaphores are used. - -+ **Minor bug fixes** - * **[XrdSecsss]** Fix memory leak in sss protocol. - * **[XrdNet]** Allow hostnames to begin with a digit. - * **[XrdCl]** Fix segfault in case a user cannot be mapped to a home directory. - * **[XrdCl]** Make sure a socket is always associated with a proper poller - object (not null). - * **[XrdCl]** Fix deadlock in XrdCl::PollerBuiltIn during finalize. - * **[XrdCrypto]** Do not use md5 checksum on OSX platform. - -+ **Miscellaneous** - * **[RPM]** Include xrdacctest in xrootd-server package. - * **[RPM]** Add conditional BuildRequires for ceph >= 11. - * **[RPM]** Use compat-openssl10-devel for fedora>=26. - * **[XrdCl]** Make sure the Log class can be used by any client plugin implementation. - -------------- -Version 4.4.0 -------------- - -+ **New Features** - * **[Server]** Add new [no]rpipa option to xrd.network directive. - * **[Server]** Allow objectid's to be specified in the authorization file. - * **[Server]** Add new logging plugin interface. - * **[Server]** Fixes #345 - add sid to TOD structure (ABI compliant). - * **[Server]** Implement resource selection affinity (primarily for ssi). - * **[XrdCl]** Add Metalink support (xrdcp & API). - * **[XrdCl]** Enable metalink processing on default. - * **[XrdCl]** xrdcp: use cks.type cgi tag to select the checksum type. - * **[XrdCl]** Support local metalink files. - * **[XrdCl]** Add support for GLFN redirector of last resort. - * **[XrdCeph]** Implemented pools of ceph objects. - -+ **Major bug fixes** - * **[Posix]** Remove double unlock of a mutex. - * **[Client]** Serialize security protocol manager to allow MT loads. - * **[Authentication/sss]** Fix dynamic id incompatibility introduced in 4.0. - * **[XtdHttp]** Removed the deprecated cipher SSLv3, in favor of TLS1.2 - * **[XrdCl]** Close file on open timeout. - * **[XrdCl]** Differentiate between a handshake and an xrootd request/response - while processing an incoming/outgoing message. - * **[XrdCl]** Fix dangling pointer issue that occurs while forking. - * **[XrdCl]** Ensure DefaultEnv is finalized after last use of the object. - -+ **Minor bug fixes** - * **[Proxy]** Avoid SEGV when printing memory cache statistics. - * **[Server]** Avoid XrdNetIF static initialization issues. - * **[Server]** Honor DFS setting when forwarding operations. - * **[Server]** Make sure lockfile time is updated in deprecated runmodeold. - * **[Server]** Fixes #344 - squash path before checking for static redirect. - * **[Server]** Free Entity before replacing it from the cache (memleak). - * **[XrdCl]** xrdfs ls does not include opaque info in a listing. - * **[XrdCl]** Eliminate unnecessary write notifications. - * **[XrdCl]** Forward xrd.* parameters from the original to the redirection URL. - * **[XrdCl]** Do not preset CWD in batch mode. - * **[XrdCl]** Be complaint with file URI scheme. - * **[XrdCl]** Fix wrong query string used for opaquefile code. - * **[XrdCl]** Translate XRootD error code to errno before passing to strerror. - * **[XrdCeph]** Fixed thread safety of filedescriptors in the ceph plugin. - * **[XrdCeph]** Protected initialization of ioCtx object and striper objects - by mutex in the ceph plugin. - * **[XrdCeph]** Fixed memory corruption in asynchronous read from ceph. - * **[XrdXml]** Make sure c-string buffes are properly terminated. - * **[XtdHttp]** Don't trim printable characters. - -+ **Miscellaneous** - * **[XrdCl]** Change the way handlers and messages are matched (use maps). - * **[Apps]** Add xrdacctest to the tools set to test access control databases. - * **[Packaging/RPM]** Set max open files limit to 65k for systemd services. - -------------- -Version 4.3.0 -------------- - -+ **New Features** - * Add option to query network configuration via configured interfaces. - * **[Proxy]** Default event loops to 3 and allow it to be set via config. - * **[Server]** Let client inform redirector why it's retrying a lookup - using the triedrc CGI element. - * **[Server]** Add cms.cidtag directive to qualify the global cluster id - (solves dpm problem). - * **[Server]** Make it possible to effeciently query an external database - for file existence via the statlib plug-in (largely for DPM).` - * **[Server]** Allow declaring extra large I/O buffers (mostly for Ceph). - * **[Server]** Allow iovec based data responses (no ABI changes). - * **[Misc]** Add back trace capability to the tool set. - * **[Misc]** Add generalized XML parsing ability. - * **[Misc]** Add metalink parsing for future integration. - * **[XrdCl]** xrdcp add env var to disable recovery - * **[XrdCl]** Add support for multiple event loops. - -+ **Major bug fixes** - * **[Server]** Correct IP address matching between IPv4 and IPv6. Fixes #300. - * **[Server]** Ruggedize cmsd thread synchronization during node deletion. - * **[Server]** Delete extraneous semaphore wait to avoid deadlock (#290). - * **[Server]** Return correct response to a delayed open. - * **[Server]** Fix build of kXR_wait message in case of delayed open. - * **[XrdCl]** Detect whether client is dual stacked based on outgoing - connection in addition to DNS registration. - * **[XrdCl]** Avoid EAGAIN loop. Fixes #303. - * **[XrdCl]** Append opaque info in case we retry at a data server after - being redirected. - * **[XrdCl]** Fix: FileStateHandler::OnOpen seqfaults when both write timeout - and OpenHandler timeout at the same time. - * **[XrdCl]** Avoid SEGV when server fails after it responds waitresp. - * **[XrdCl]** Continue processing remaining files after error occurrence. - * **[XrdCl]** Fix for dangling pointer problem in deep locate, fixes #324 - * **[Misc]** Add possibility to specify disk usage parameters in .. G, T units - using XrdOuca2x::a2sz(). - * **[Python]** Fix lock inversion in python bindings. - * **[Python]** Check if python interpreter is still initialized. - -+ **Minor bug fixes** - * **[All]** Fix numerous issues with space reporting (spaceinfo, query space, - statvfs) such a double counting, scaling, and format issues. - * **[Proxy]** Do not use the ffs code path if nothing is writable. This avoids - initialization failure when the origin is a large WAN cluster. - * **[Server]** Be agnostc NTP defaults when rotating logs (fixes new RH7 defaults). - * **[Server]** Pass correct total data length in iovec to Send(). - * **[Server]** Avoid redirection loop during error recovery in a uniform cluster. - * **[Server]** Make sure N2N gets configured for the cmsd when actually needed. - * **[Server]** Properly handle an inifit NPROC limit. Fixes #288. - * **[Server]** Make sure the cluster ID is always formatted the same way. - * **[Server]** Correctly compute timeout wait. - * **[Server/Logrotate]** Make sure rotating pattern is not expanded in an if statement, fixes #302 - * **[Misc]** Include XrdXmlReader in the spec file. - * **[Misc]** Fix bug in access statistics print. - * **[Misc]** Allow conversion of decimal numbers in XrdOuca2x::a2sz() - * **[XrdCl]** xrdfs prepare has to be provided with a filename, fixes #309 - * **[XrdCl]** Make sure recursive copy is disallowed only for checksum with user provided value, fixes #304 - * **[XrdCl]** Use the same timeout value for all close operations in xrdcp with TPC enabled. - * **[XrdCeph]** Fixed race condition in multistream access to files fo CEPH - -+ **Miscellaneous** - * **[Server]** Prevent cmsd reconnect storm when things get way slow. - * **[Server]** Changes to allow for Solaris compilation. - * **[Server]** Changes to allow for OSX compilation. - * **[Server]** Detect cyclic DNS host registration when processing '+' hosts. - * **[Server]** Display manager IP addresses during '+' host resolution. - * **[Util]** Avoid compiler warning about unsafe mktemp. - * **[App]** Do not report expected errors as errors. - * **[App]** Always show any unusual node status in the display. - * **[Client/Python]** Add MANIFEST.in for python bindings. - * **[XrdFileCache]** Implement blacklisting in a FileCache decision plugin. - * **[XrdFileCache]** Make sure requested offset is reasonable. - * **[XrdFileCache]** Return -1 and set errno when bad offset is passed in. - * **[XrdFileCache]** Only generate error for negative offsets, as per posix. - * **[XrdFileCache]** Add startup protection for ReadV, too. It was already there for Read. - * **[XrdFileCache]** Fix bug in cache scanning; simplify deletion loop. - * **[XrdFileCache]** Use bytes to calculate how many files to purge, not blocks; - subtract actual size of the file, not the length of it returned by stat. - * **[XrdFileCache]** In cache purge, use stat.mtime of cinfo file if last access time can not - be determined from contents of cinfo file. - * **[XrdFileCache]** Fix argument type from int to long long (was n_blocks, is size_in_bytes now). - * **[XrdCl/XrdSys]** Use custom semaphores only for glibc<2.21. - * **[XrdCl]** Remove libevent-based poller implementaion. - * **[XrdCl]** Report reason for reselection via triedrc CGI element. - * **[XrdClient]** Changes to allow for Fedora rawhide C++11 compilation. - * **[XrdCeph]** Fixed XrdCeph compilation for C++11 enabled compilers - * **[XrdCeph/CMake]** Fix for undefined symbols (link XrdUtils). - -------------- -Version 4.2.3 -------------- - -+ **Major bug fixes** - * **[Server]** Avoid SEGV if cmsd login fails very early. - * **[Server]** Avoid SEGV when an excessively long readv vector is presented. - * **[Server]** Rationalize non-specfic locate requests. - * **[XrdCl]** Process waitresp synchronously via Ignore return to avoid SEGV. - * **[XrdCl]** Avoid memory leak when a handler returns Ignore for a taken message. - * **[XrdCl]** Fix "tried" logic by forwarding the errNo - -------------- -Version 4.2.2 -------------- - -+ **Major bug fixes** - * **[Proxy]** Protect forwarding proxy server from slow connections. This should - fix most, if not all, SEGV's that the server encountered under heavy load. - * **[Server]** Fixes #248 Prevent infinite loop when shift arg is negative. - * **[Server]** Complain when passed I/O length is negative. - * **[Server]** Avoid execution stall during node logout when the thread limit - has been reached. - * **[Server]** Make sure to capture return code for stat() to prevent random - results. - * **[XrdCl]** Make sure to get filestate lock during timeout processing to - avoid MT intereference and possible random results. - * **[XrdClient]** Restore commented out abort() when an attemp is made to index a - vector outside of its current bounds (avoids random results). - * **[Server/Proxy]** Delay deleting a file object if the close was not successful. - This avoids deleting objects that may have pending activity resulting in an - eventual SEGV. This is a bypass fix to another problem. - -+ **Minor bug fixes** - * **[Server]** Fixes #234 Properly register all components in a mkpath request. - * Correctly handle copying into a non-existent directory when automatic - path creation is enabled. - * **[XrdCl]** xrdfs correctly handles quotations (fixes the problem with ALICE token) - -+ **Miscellaneous** - * Fixes #245 Provide compatibility when cmake version is > 3.0. - * Use atomics to manipulate unlocked variable pollNum. - * Bugfix: release lock when a file is closed before the prefetch thread is started. - Observed with xrdcp ran without -f option and an existing local file. Fixes #239. - * Protect from reads exceeding file size. Fixes #249. - * Release Stream lock before invoking callbacks. Fixes #216 - * TPC: Fix deadlock in case of error in the TPC authentication - * Increase max size of write to disk queues. - * Fix bug in endswith. Fixes #260 - * XrdCeph : fixed problem with files bigger than 2GB for synchronous writes - * **[XrdCl]** Change message loglevel from Error to Debug. Fixes #246. - * **[XrdCl]** Fix race condition in PostMaster initialization - * **[XrdCl]** Provide atomicity for PostMaster value using built-in functions - * **[XrdFileCache]** fixed deadlock on immediate file close (e.g. xrdcp to non-writable output) - * **[XrdFileCache]** fixed errors on some posix operations using virtual mount - -------------- -Version 4.2.1 -------------- - -+ **Miscellaneous** - * **[Client/Cl]** Make sure kXR_mkpath is set for classic copy jobs when the - destination is xrootd (backward compatibility fix). - -------------- -Version 4.2.0 -------------- - -+ **New Features** - * **[Client/Python]** Integrate xrootd-python into the main package. - * **[Server]** Include a Ceph OSS plug-ing. - * **[Server]** Implement throttling. - * **[Server]** Detect redirect loops using "tried" token. - * **[Server]** Implement the "cid" option for config query to display the - unique cluster ID. - * **[Server]** Allow suspending and enabling remote debugging without a - restart. - * **[Server]** Implement black/whitelist with optional redirection. - * **[Server/Proxy]** Add the xrdpfc_print tool to print the caching - proxy metadata. - * **[Server/PlugIns]** Provide a mechanism to pass command line arguments - to plug-ins. - * **[Server/PlugIns]** Provide access to the native and the active extended - attribute implementation. -+ **Major bug fixes** - * **[All]** Fix various memory access issues. - * **[Server]** Fix various IPv4/IPv6 compatibility issues. - * **[Server]** Avoid disabling of frm notifications due to plug-in - initialization issues. - * **[Server/Proxy]** Avoid holding a global lock when opening/closing files - to solve timeout issues. - * **[Security/GSI]** Fix reloading of CA and CRLs. -+ **Minor bug fixrs** - * **[Server/HTTP]** Fix issues related to invalid chunk sizes. - * **[Server/Proxy]** Various logic and permission processing fixes. -+ **Miscellaneous** - * **[Client/Cl]** Make the compiler issue warnings when the return codes - from the File and FileSystem methods are unchecked. (issue #188) - * **[RPM]** Disable building of the compat package by default. - * **[Server/Proxy]** Avoid serializing stat() via the proxy to improve - performance. - * **[Tests]** Factor out the common testing code from the client tests so - that it can be re-used. - -------------- -Version 4.1.2 -------------- - -+ **Major bug fixes** - * **[Utils]** Don't confuse -I and --tpc while parsing commandline parameters - for xrdcp. (issue #213) - * **[Server]** Fix various IPv4/IPv6 issues. (issues #164, #227) -+ **Minor bug fixes** - * **[Client/Cl]** Print mtime when doing xrdfs stat. - * **[All]** Fix some memory access issues. (issues #186, #197, #205) - * **[Server]** Recreate logfile fifo if it already exists and is a file. - (issue #183) - * **[Server]** Properly reset suspend state when reconnecting cmsd. - (issue #218) - * **[Server]** Avoid disabling async I/O when using an oss plugin that does - not implement file compression. (issue #219) - * **[Server]** Do not debit space when relocating a file within the same - partition. - * **[Server]** Fix meta-manager port directive ordering. - * **[Server/Logrotate]** Do not print anything to stdout to avoid making cron - send emails to admins. (issue #221) -+ **Miscellaneous** - * **[Server/Proxy]** Disable POSC processing when a proxy plugin is loaded. - -------------- -Version 4.1.1 -------------- - -+ **Major bug fixes** - * **[RPM]** Remove the library patch from xrootd-config to enable multiarch - installations. - * **[RPM]** Move the user creation scriptlets to xrootd-server where they - belong. (issue #179) - * **[Server]** Fix PowerPC compilation. (issue #177) - * **[Server]** Avoid the pitfalls of infinite nproc hard limit in Linux. - * **[Server]** Correct flag definition to include cms plugin loading. (issue #176) -+ **Miscellaneous** - * **[Man]** Update documentation. - * **[Client/Cl]** Set the multi-protocol ability basing on an environment - variable. - -------------- -Version 4.1.0 -------------- - -+ **New Features** - * **[Everyting]** Implement dynamic plugin shared library filename versioning - to allow multiple major versions to co-exist. - * **[Server]** Compelete IPv6/IPv6 and public/private network routing. - * **[Server]** Allow the checksum manager to use OSS layer to access data. - (issue #140) - * **[Server]** Allow the definition of subordinate clusters. - * **[Server]** Support multiple checksum types. Client can select non-default - checksum using the "cks.type=" cgi element. - * **[Server]** Provide plugin interface for handling extended attributes. - * **[Server]** Add options to xrd.network to control keepalive. - * **[Server]** Control core file generation via xrd.sched core directive. - * **[Server]** Add pss.permit directive to restrict outbound connections for - forwarding proxies. - * **[Server]** Allow xrootd to handle objectid names as exports. - * **[Server]** Install and package the cluster mapping utility: xrdmapc. - * **[Server]** Allow the specification of xrootd.seclib default. - * **[Server]** Pass along XRD_MONINFO setting and application name to - monitoring. - * **[Server/Proxy]** Implement a forwarding proxy option. - * **[Server/Proxy]** New configuration of XrdFileCache using 'pfc.' prefix. - * **[Sever/HTTP]** Support gridmap parsing. - * **[Client/Cl]** Inform the server about availability of local IP address - types (IPv6/IPv4, public/private) to in order to facilitate redirections. - * **[Client/Cl]** Make the client send kXR_endsess request when recovering - broken connection - avoids 'file already open' errors. - * **[Client/Cl]** Implement TCP keep-alive support. - * **[Client/Cl/xrdcp]** Optimize xrdcp uploads by compensating for latency. - * **[Client/Cl/xrdcp]** Make it possible for xrdcp to run multiple transfers - in parallel using the '--parallel' option. - * **[Client/Cl/xrdcp]** Make it possible for xrdcp to concatenate multiple - sources to stdout. - * **[Client/Cl/xrdfs]** Add xrdfs locate -i option to ignore network - dependencies (IPv6/IPv4). - * **[Security]** Add new security framework loader to allow external pacakges - that linked against security plugins to dynamically load them instead. - * **[Security/sss]** Allow forwardable sss tokens when ecrypted with a - forwarding key as defined by the xrdsssadmin command. - * **[Plugins]** Implement generic matching rules to version check 3rd party - plug-ins. - * **[Packaging/RPM]** Add SystemD configuration files for RHEL7. - * **[Packaging/RPM]** Introduce compat RPM packaging providing xrootd 3.3.6 - deamons and libraries with the ability to switch between desired versions - using the sysconfig file. - * **[Packaging/RPM]** The RPM naming has been switched back to xrootd - (from xrootd4). - * **[Utils]** Add xrootd-config utility. - -+ **Major bug fixes** - * **[Server/HTTP]** Make it possible to handle files larger than 2GB. - * **[Server]** Prevent blacklisting of all connctions when role is supervisor. - * **[Server]** Fix bug in handling cms.dfs redirect verify that would keep - the client is an infinite wait loop. This also affected locate requests - regardless of what the redirect option was set to. - * **[Server/Proxy]** Avoid SEGV when no environment has been passed in the - proxy server. - -+ **Minor bug fixes** - * **[C++ API]** Provide complete portability and correct behaviour across - platforms with and without Atomics. This patch does not change any ABI's. - * **[Server]** Do not set *TCP_NODELAY* for unix domain sockets as this - issues a nasty error message. - * **[Server]** Allow cms.dfs mdhold argument to be 0 as documented. - * **[Server/Plugins]** Add missing initializer to the LocInfo structure. - * **[Server/Plugins]** Correct header define gaurd in XrdSfsFlags.hh. - * **[Server/Proxy]** Fully support extended file system features and pass - those features through a proxy server. (issue #115) - * **[Client/Cl]** Remove duplicates from the HostList. - * **[Client/Cl]** Fix minor atomicity issues (C++11). - -+ **Miscellaneous** - * **[Server]** Actually remove xmi plugin handling as xmilib is no longer - supported. - * **[Server]** Make sure to always passhrough CGI information. - * **[Server]** Honor network routing when creating the client's i/f - selection mask. - * **[Server]** Efficiently handle replicated subscribers (i.e. managers). - * **[Server/HTTP]** Remove useless loading the security framework. - * **[Server/Security]** Add new NetSecurity::Authorize() method that accepts - text. - * **[Server/Proxy]** Properly support proxying objectids. - * **[Server/Proxy]** Clean-ups in the caching proxy. - -------------- -Version 4.0.4 -------------- - -* **Major bug fixes** - * **[Client/Cl]** Properly allocate buffers for error messages. (issue #136) - * **[Client/Cl]** Check if there is enough data before unmarshalling. - * **[Client/Cl]** Fix a memory leak in MessageUtils::WaitForResponse - affecting all synchronous calls. - * **[Client/Cl]** Prevent a segfault in the destructor when called after - the libXrdCl library has been finalized by the linker - ROOT garbage - collection. https://github.com/cms-externals/xrootd/pull/1 - * **[Client/Posix]** Fix broken readdir_r() and readdir_r64() functions. - * **[Server]** Use correct flag when adding a cluster. The bug made it - impossible to have more than one supervisor node. - * **[Server/Logrotate]** Prevent stack corruption by correctly sizing the - timestamp buffer. - -+ **Minor bug fixes** - * **[Client/Cl]** Properly check if a recursive copy was requested to avoid - unnecessarily stating the source. - * **[Client/Cl]** Avoid inserting duplicate entries to HostList when retrying - at the same server. - * **[Client/Cl]** Normalize (trim leading zeroes) before comparing adler and - crc checksums. (issue #139) - * **[Client/Posix]** Prevent mkdir failure in a clustered environment by - creating the full directory path by default. - * **[Client/Possix]** Fix a memory leak when doing deep locate. - * **[Server/Logrotate]** Use expect to send a ping to pipes. This prevents - logrotate from hanging when nobody is listening at the other end of the - pipe. - * **[Authentication/Client]** Pass the external environment to the protocol - manager. (issue #133) - * **[Authentication/sss]** Fix a memory leak. - * **[Utils]** Avoid SEGV when assigning a unix domain address to a - NetAddrInfo object previously used to hold a TCP domain address. - * **[Server/cmsd]** Use the same write selection rules for dfs and non-dfs - environments. - -+ **Miscellaneous** - * **[Server/Logrotate]** Prevent the default configuration from sending - emails to admins and from creating a new log after the old one has - been rotated. (issue #135) - * **[Server/SELinux]** Using expect in logrotate requires the logrotate_t - context to have access to pseudoterminals and tmpfs as well as stating - fifos - * **[Client/Commandline Parser]** Allow local to local copy in new xrdcp but - not in the old one. - * **[Client/Cl]** Discard a whole cluster on failure in federation context. - (issue #132) - -------------- -Version 4.0.3 -------------- - -+ **Major bug fixes** - * **[Server]** Make sure the network routing is honored in all cases. This - fixes problems encountered by sites whose clients use a private IP address - to connect to a redirector's public IP address. (issue #130) - -------------- -Version 4.0.2 -------------- - -+ **Minor bug fixes** - * **[Client/Cl]** Handle all non-NULL-terminated error responses correctly. - * **[Client/Cl]** Release old auth buffer when reconnecting after TTL - expiration. - -+ **Miscellaneous** - * **[Client/Cl]** Retry after an incomplete local write. This produces - clearer error messages. Ie: "Run: [ERROR] OS Error: No space left on - device" instead of: "Run: [ERROR] OS Error: Operation now in progress". - * **[Client/Cl]** Don't force a server to issue a short read when fetching - last data chunk. This works around issues for proxied FAX sites. - -------------- -Version 4.0.1 -------------- - -+ **Major bug fixes** - * **[Server]** Prohibit accessing memory via /proc using digFS. - -+ **Minor bug fixes** - * **[Server]** Prevent over-scan of the xrd.network routes option which may cause - a config file error message and initialization failure. - * **[Server]** Fixes to make things compile on ix86, arm and ppc64. - * **[Server]** Correct protocol name supplied to monitoring for userid. - * **[Server/Proxy]** Various minor fixes to caching proxy. - * **[Security]** Check the length before looking inside a SUT buffer. (issue #126) - * **[Client/Cl]** Check for copy source and target validity to display proper error - messages. - * **[Client/Cl]** Return default plug-in factory for an empty URL. (issue #120) - * **[Client/Posix]** Provide full error mapping for POSIX interface. - * **[All]** Remove some unnecessary commas and semicolons. (issue #121) - -+ **Miscellaneous** - * **[Server]** Pass client login information to monitoring. - * **[Client/Cl]** Make xrdfs locate -h synonymous to locate -m. - * **[Client/Cl]** Add -i option to xrdfs locate setting the Force flag. - * **[Docs]** Various documentation updates. - -------------- -Version 4.0.0 -------------- - -+ **New Features** - * Supprt IPv6. Please read docs/README_IPV4_To_IPV6 for details. - * Introduce the XrdFileCache library - a proxy server plugin used for caching - of data into local files. - * Beta support HTTP(S). - * Provide protocol bridge to let other protocols use xrootd back-end plugins. - * Provide full support for public/private IP networks. - * Allow remote debugging via the xrootd.diglib directive. - * Provide a mechanism to manually control log file rotation via -k and add - support for logrotate. - * Add -z option to enable high recision log file timestamps. - * Define a new plug-in to allow replacement of the stat() function when - used to determine exported file characteristics. This plug-in is meant - to be used by tape-backed file systems that identify offline files in - odd ways (e.g. GPFS). Patch assumes XRDROLE patch below. - * Implement full readv-passthru for enhanced performance. - * Add a disconnect record to the f-stream. - * xrdcp is now the same as xrdcopy, and old xrdcp is now xrdcp-old - * Make clients configurable via /etc/xrootd/client.conf and - ~/.xrootd/client.conf - * Implement a plug-in system for client's File and FileSystem queries. - * Make it possible for 'xrdfs stat' to query for combination of flags. - * Make third party copies cancellable. - * Implement xrdfs spaceinfo, cat and tail commands - * Terminate iddle connections after a timeout and treat timeouts on streams - that should be active (because of outstanding requests with no delay times) - as errors. - * Implement XrdCl::File::Visa and XrdCl::File::Fcntl. - * Support for full URL redirects. - * File and Filesystem objects implement property system to pass custom - information to and from them (including plug-ins) without breaking - ABI. - * Add --dynamic-src to xrdcp options to allow dynamic file copying. - * Implement the directory listing in bulk. - * Enable locate to return host names not just IP addreses. - * Implement node blacklisting for the cmsd (see cms.blacklist directive). - * Add mv command to frm_admin. - * Allow query of current role and dynamic cms state via kXR_query. - * Implement query config chksum to return supported chksum name. - * Add version as a variable that can be returned by kXR_Qconfig. - * Add sitename as an argument to kXR_Query+kXR_Qconfig. - * Provide disconnect notifiation to underlying file system. - * Provide the filesystem plugin a way of creating a session storage area. - * Add flag to indicates a secondary copy of a file exists. - * Allow testing for undefined set/env vars via if-else-fi. - * Add '-L' flag to the xrootd command to allow loading a protocol library - * Add flag to indicates a secondary copy of a file exists - - -+ **Bug fixes** - * Fix various dead locks in the IOEvents poller. - * Implement LinuxSemaphore class in order to replace buggy POSIX semaphores - on Linux. - * Honor the cmsd.dfs directive for locate request to avoid placing a - file in ENOENT status. - * Make sure that the old client runs only in IPv4 mode as mixing modes does - not work for a variety of reasons. - * Accept old-style as well as new-style IPv6 addresses in the sss - protocol. This allows the new client to use this protocol after - it implemented IPv6 support. - * Prevent invalid mutex operations in auto-termination routine. - * Resolve naming conflicts within the frm that resulted from the - statlib plugin implementation. - * Do not rely in file locking to serialize inter-thread access. This - fixes the prolem of usage file drift. - * Fix various parse context issues in copy config with --recursive. - * Recognize object deletion in the error handling path. - * Use atomic FD_CLOEXEC where available to prevent FD leaks. - * Squelch casting complaints from C++11. - * Make sure to return all nodes in a star locate request. - * Always load protocols in the specified order. - * Fix xrootdfs wcache crashing issue when using virtual file descriptor. - * Fix selection of a server when a DNS entry resolves to more than one. - * Correct pthread_cond_timedwait() time calculation and error handling. - * Fix null insertion of hostname in error message when open fails. - * Fix issues with extensions in GSI proxies - * Fix problem with creation of the forwarded KRB5 ticket - * Correctly handle reading of a partial readv headers (issue #45) - * Make sure to propagate username and password when redirecting - * Honor request timeouts when processing kXR_wait - -+ **Miscellaneous** - * XrdClient and associated commandline utilities are now obsoleted. - * Propagate info about partial success from deeplocate to dirlist. - * Remove perl interface. - * Send timezone, country code and application name while logging in. - * Change interfaces to copy process to use property system (allows for - adding features without breaking the ABI). - * Final change to f-stream monitoring. Replace standard deviation - (sdv) calc with reporting sum of squares (ssq) counts. - * Make public headers compile cleanly with -Wall -Wextra -Werror. - * Support passing cert, key paths via URLs - * Allow testing of undefined set/env vars via if-else-fi - * Pass user environment settings settings in the login CGI - * Use DNS names instead of addresses for kXR_locate when listing - -------------- -Version 3.3.6 -------------- - -+ **Minor bug fixes** - * Prevent SEGV when error occurs during stat (issue #70) - * Prevent SEGV in redirect monitoring (issue #61) - * Set reasonable linux thread limit and warn it we cannot do so. - -+ **Miscellaneous** - * Support for C++11 (narrowing fixes, unique_ptr vs. auto_ptr) - * Support for CMake 2.8.12 (interface link libraries) - -------------- -Version 3.3.5 -------------- - -+ **Minor bug fixes** - * Fix minor Coverity issues in XrdCl - * Fix a rarely occuring segfault when forking XrdCl under heavy load - * Fix various issues related to group name retrieval (issues #51, #52, #53) - -+ **Miscellaneous** - * Make XrdSys/XrdSysIOEvents.hh private - could not have been used anyways - * Add a sysconfig template to preload custom allocators in order to fix - memory issues on RHEL6 - * Allow up to 63 characters for a site name - -------------- -Version 3.3.4 -------------- - -+ **Major bug fixes** - * Serialize sss authentication client initialization to prevent race - conditions - * Actually cancel the JobManager threads while stopping it - this affected - client side fork handling (new client) - * Restore original meaning of -adler and -md5 to xrdcp (issue #44) - -+ **Minor bug fixes** - * Append CGI info when retrying at a server that handshaked but never - respnded to the request (xrdcp) - * Do socket accepts asynchronously to prevent DNS resolution from blocking - accepts (issue #33) - * Warn about incomplete dirlist responses (xrdfs) - * Cast the utilization statistics to uint16_t before printing to - print actual numbers instead of letters corresponding to ASCII codes - (xrdfs) - -+ **Miscellaneous** - * When calling File::Stat use file handle instead of path - * Improve handling of malformed kXR_readv responses (new client) - * Explain parameters of xrdcopy --tpc (documentation, issue #46) - -------------- -Version 3.3.3 -------------- - -+ **Major bug fixes** - * Prevent SEGV's when reusing a recycled protocol object under certain - conditions (xrootd server) - * Prevent SEGV when using the -DS/-DI commandline parameters in xrdcp - (issue #13) - * Prevent integer overflow when calculating client recovery windows - * Make sure the new client tries all available authentication protocols - when connecting to a security enabled server (issue #14) - * Detect buffer size mis-matches when server returned valid response with - invalid size (xrdcopy) - * Recognize /dev/null and /dev/zero as special files when using copy - commands - -+ **Minor bug fixes** - * Prevent the new client deadlock on Solaris and MacOS when using - the built-in poller and connecting to localhost (issue #5) - * Compensate for ROOT garbage colletion issues when calling the - new client code - * Avoid favoring socket writes when using new client with the built-in - poller - * Strip off opaque information from dest filename when copying to local - filesystem using xrdcp (issue #21) - * Fix setting client timeout resolution while connecting to a server - -+ **Miscellaneous** - * Change the RPM package layout to match the one used by EPEL (issue #12) - * Drop the daemon user RPMs - * Allow new client connection parameters to be tweaked by connection URL CGI - * Make the built-in poller default again in the new client - after resolving - issue #5 - -------------- -Version 3.3.2 -------------- -+ **Major bug fixes** - * Fix the opaque information setting in xrdcp using -OD (issue #1) - * Fix compilation on Solaris 11 (issue #7) - * Fix issues with semaphore locking during thread cancellation on - MaxOSX (issue #10) - * Solve locking problems in the built-in poller (issue #4) - * Solve performance issues in the new client. Note: this actually - changes some low level public interfaces, so the soname of - libXrdCl.so has been bumped to libXrdCl.so.1. The xrootd.org - RPMs also provide the old libXrdCl.so.0 in order to preserve the - binary compatibility with the clients linked against it. - -------------- -Version 3.3.1 -------------- -+ **Major bug fixes** - * Correct XrdClient ABI incompatibility issue introduced in 3.3.0 - * Install additional private headers - -------------- -Version 3.3.0 -------------- -+ **New Features** - * Stable interfaces immutable in minor releases (except XrdCl). Only - public header files are installed in the usual include directory. - In order to ease up transition of some clients some of the private - include files are also installed in private subdirectory. - * New asynchronous and thread-safe client libraries and executables - (XrdCl). The ABI compatibility is not guaranteed until 4.0.0. - * Build the xrootd protocol plugin as a shared library. - * Add the altds directive to allow pairing a cmsd with an alternate data - server. - * Differentiate between packed and unpacked readv monitoring records. - * Allow plugin libraries to be preloaded. This feature is only meant - for MacOS. - * Include optional site name in summary monitoring records. - * Include optional site name in server identification record if the - site name was specified on the command line (-S) or via config - file (all.sitename directive). - * Define a standard supported mechanism to obtain the default storage - system object. - * Provide an ABI-compatible interface to obtain a default cmsd client - object. This patch does not change the definition of the XrdCmsClient - object and is ABI compatible with all previous releases (DPM support). - * Allow multiple comma separated protocols in XrdSecPROTOCOL client-side - envar. This allows the client to select 1 of n protocols. - * Implement new "f" stream monitoring. - * Add new summary counters for readv and readv segs. - * Add boiler plate comments indicating the all software is licensed under - LGPL. No functional source code was modified by this patch. - * Add GPL and LGPL license text. - * Liberlize locking structure to prevent lock inversion relative to - external locks. - * Provide libevent replacement for Linux (epoll), Solaris (poll_create), - and others (poll). Note: versions of Solaris less than 10 are no longer - supported and they will no longer compile with this update! - * Provide a libevent type replacement package. - * Allow tracker files (e.g. ".fail") to be placed in a shadow directory. - This is controlled by the new fdir option on the oss.xfr directive. - * Allow meta-files (i.e. .fail file) to be relocated to a shadow directory - using the oss.xfr directive. This avoids polluting the exported name - space when an frm transfer operation fails. - * Create a general place for platform dependent utility methods. - * Add third party copy statistics to the summary record. - * zlib compatible checksum plugin - -+ **Major bug fixes** - * Serialize access to cache entries to prevent SEGV's. - * Fix the fast response queue so that it doesn't run out of response - slots causing a big performance penalty. This is a high priority fix. - * Properly disarm the mutex helper when the mustex object is deleted. - * Use correct variable to hold osslib parameters. This patch fixes commit - 2e27f87a (version checking) and without this patch makes it impossible - to load an oss plug-in. - * Properly check for errors when client read returns 0 and reflect true - status. This only affects the Posix client interface. - * Remove redundant flag indicating a running poller. This may cause the - poller to never be woken up when a timeout value changes. - * Fix tag in ofs statistics. It is improperly terminated and may - cause certain xml parsers to fail; rendering monitoring useless. - * Undo the side-effect of commit ff8bdbd6 that prevented the frm from - sending stage notifications to xrootd; causing opens and xrdstagetool - to hang with dynamic staging enabled. - * Make sure the id buffer is large enough to hold all id combinations. - * Avoid deadlock when closing a Posix File with an active preread. - * For concurrent queries for the same file allow servers to respond to the - query and only redirect clients to a stageable server if the file is not found. - -+ **Minor bug fixes** - * Add EPOLLRDHUP to avoid leaving sockets in CLOSE_WAIT with a one-shot - poll framework. - * Fully integrate checksum processing into a manager node. When configured, - it does not matter whether a client directs a checksum request to a manager - or a server. This also fixes bug report #93388. - * Make sure to reflect proper range of errors during read/write operations. - This also provides filesystem plugins full range of allowed return codes. - * Initialize the rMon toggle to avoid valgrind complaint. - * Fix minor issues reported by Coverity. - * Make sure opendir() returns a null pointer when the directory doesn't - exist. - * Make sure that XrootdFS returns ENOENT when opendir() returns a null. - * Make sure to use correct time to set mtime/atime after a physical reloc. - * Prevent hangs when doing exterme copy from server to server. - * Fix the -force option to really work for the mark subcommand. - * Pass through error code returned by the N2N plug-in. This only affects - the proxy server and caused feature interference. - * Automatically exclude originating server/cluster on an enoent static - redirect. - * Correct typos XRDPSOIX envars should really be named XRDPOSIX. - -+ **Miscellaneous** - * Remove superfluous includes or other move includes to eliminate - unnecessary dependencies in ".hh" files. This patch is required - to create an EPEL conformable include directory. - * Add port to prepare request struct as documented in 2.9.9. - * Add pathid to readv request struct as documented in 2.9.9. - -------------- -Version 3.2.6 -------------- -+ **Major bug fixes** - * GSI authentication: fix possible race condition while re-loading CA - certificates; fix also related memory leaks. - * GSI authentication: make sure the CA cache is not initialized twice (e.g. - server and client inside there), and that the cache entry pointers are - always initialized. - * Crypto OpenSSL modules: use more appropriate way to read the RSA complete key, - solving various issues for RH6 and derivations, included SL(C)6. - * Make sure redirect opaque information is passed along for all filename - based requests. This is required for DPM and EOS N2N services to work - in all cases (most importantly, stat). - * Make sure buffer ends with null byte before read suspension. This only - occurs on very heavily loaded connections. - * Fix the fast response queue so that it doesn't run out of response - slots causing a big performance penalty. This is a high priority fix. - -+ **Minor bug fixes** - * Properly detect external process failure and report correct error status - to a client. This also fixes bug report #91141. - * [XRootDPosix] Make sure to use a supplied cache even when no cache - directives given. - * Make sure to return a usable path string via XrdOucCacheIO::Path(). - * Actually support 4 different redirect destinations. - -+ **Miscellaneous** - * Transparent support for new name hashing algorithm adopted in openssl - 1.0.0x (GSI authentication protocol) - * Verbosity levels revised for GSI and PWD authentication protocols. - * Notification of initialization option for GSI and PWD authentication - protocols. - * Do not repudiate file existence on an "cancelled" error during open. - this patch addresses overloaded dCache pool nodes. - -------------- -Version 3.2.5 -------------- -+ **Major bug fixes** - * Make realoading gridmapfile atomic (protect from segfault) - * Propagate to clients proper range of errors during read/write operations - * Fix segfault when handling writes to files that have not been opened - -------------- -Version 3.2.4 -------------- -+ **Major bug fixes** - * Work around a dead-lock in the client fork handlers. - -------------- -Version 3.2.3 -------------- -+ **Major bug fixes** - * Make sure read statistics are updated for sendfile() and mmap I/O. - * Make sure refresh thread is dead before deleting deleting the keytab to - avoid SEGV's. - * Add missing include for compiling with gcc-4.7 (from Sebastien Binet). - This patch is required for successful compilation. - * Avoid segfaults when limiting number of redirections caused by failed - authorization. - * Avoid deadlock in the client fork handlers. - -+ **Minor bug fixes** - * Correct monitor initialization test to start monitor under all configs. - * Fix a memory leak in the client handshake algorithm. - -+ **Miscellaneous** - * Make RHEL6-created SRPMs buildable on RHEL5 by forcing RPM to use MD5 - digests. - * Fuse: Use default TTL values for data server connection and load - balance server connection. - -------------- -Version 3.2.2 -------------- -+ **Major bug fixes** - * Correct test whether or not to initialize redirect monitoring. The old - code never initialized it this disabling redirect monitoring. - * Backport frm notification fix that stalled stage-in requests from commit - 69e38cfd6b8bb024dd34f8eb28a666fbf97f346b - * Prevent SEGV when xrd.monitor rbuff value not specified - * Prevent xrdcp hangs when doing exterme copy from server to server. - * In case of 'limited proxy' look for VOMS attributes also in the parent - proxy. - * Correct log processing for sites that use the root directory as the - stomping ground for newly created files. - -------------- -Version 3.2.1 -------------- -+ **Major bug fixes** - * Don't build sendfile support on MacOSX because it doesn't work - * Prevent double-free abort when more than 16 files have been opened by a - client and the client terminates the session without closing the 17th one. - -------------- -Version 3.2.0 -------------- -+ **New Features** - * Retool the XrdOucCache object so that cache implementations can be - implemented as plugins. - * Add FSize method to the XrdOucCacheIO object to ease implementation - of disk caches containing partial files. - * Add the pss.cachelib directive to specify a cache plugin. - * Implement ultralow overhead redirect monitoring. - WARNING: ofs plugin writers will need to recompile their plugin interface - to be fully compatible with this commit due to additional - information passed to the ofs object "new" methods. - * Allow the XrdCmsClient interface (a.k.a Finder) to be a plug-in. - * Add ofs.cmslib directive to specify the XrdCmsClient plug-in. - * Add new class, XrdOucCallBack, to simplify using callbacks in the - XrdCmsClient plug-in. - * Define the frm.all.monitor directive to enable migration, purging, and - staging monitoring. This was originally part of xrootd.monitor but that - just was odd. Note that the stage, purge, migr events are no longer - accepted on the xrootd.monitor directive. - * Collapse he staging (s) and migration (m) records into a single transfer - (x) record. While not compatible, the previous implementation was new - code and no one actually was capturing these records. - * Implement a server identification record (=) that unquely identifies each - server. The record can be sent periodically and can be used as a heartbeat. - * Add -y option to xrdcp to limit number of extreme copy sources. - * Uniformly pass the execution environment to all oss and cms client - methods. This is largely for DPM support. - WARNING: While this update is binary backwad compatible to existing oss - plug-ins it is not source compatible. Plug-in writers will need - to modify their oss methods to successfully compile. - * Allow an automatic redirect when a file operation ends with ENOENT. - Allow redirects for chsum and trunc operations. - Both of the above are controlled via the xrootd.redirect directive. - * Report the timezone when connecting to a [meta]manager. - * Allow configuration of staging, migration, and purging events. - * Allow transfer script to inject information into the monitoring stream. - * Report number of attempted login, authentication failures, successful - authenticated and unauthenticated logins in the summary statistics. - * Indicate whether a disconnect was forced and whether it was a parallel - path (as opposed to a control path) in the monitoring record. - -+ **Major bug fixes** - * Provide compatibility for sprintf() implementations that check output - buffer length. This currently only affects gentoo and Ubuntu Linux. - We place it in the "major" section as it causes run-time errors there. - * Reinsert buffer size calculation that was mistakenly deleted. - This eventually causes a SEGV when detailed monitoring is enabled. - * Remove improper initialization that may cause a SEGV in the checksum - manager. - * Add missing initializer without which we will get a SEGV. This is a fix - for the just added monitoring code. - * Remove regressions that prevent a proxy cluster from being fully - configured. - -+ **Minor bug fixes** - * Correct debug message frequency that caused people to think some file - system partitions were being ignored. - * Correct pthread Num() to return thread-specific numbers. - * Make sure the sendfile interrupt counter is initialized to zero. - * Make sure to honor absolute cms.space values when percentage not - specified. - * Prevent double user map record when monitoring when auth is configured - but not actually monitored. - * Take timezone changes into account when waiting for midnight. This solves - the log rolling problem when changing between DST and standard time. - * Make sure to cut close records for open files during a forced disconnect - when monitoring file information. - * Do not create meta-files or update extended attributes when placing a - file into read-only space. - -+ **Miscellaneous** - * Bonjour code dropped - * Complete implementation of the fstat() version of stat(). - * Consistently pass the enviroment to the cms client enterface. - * Make return codes consistent between synchronous & async XrdCmsClient - returns. - * Document the XrdCmsClient interface in the header file. - * Cut close monitor records before cutting the disconnect record. - * Make frm_purged and frm_xfrd use sparate log files. - -------------- -Version 3.1.1 -------------- - -+ **New Features** - * Compile on Solaris 11 - * Add support for sending DN with monitoring information - * Add possibility to switch off automatic download of CRL from the web; - default is OFF; to enable it multiply by 10 the relevant CRL options - (i.e. 12 and 13 are like 2 and 3 but trying download if the file is not - found). - * Add refresh frequency time for CRL's; default 1 day . - -+ **Major bug fixes** - * Fix various client threading issues. - * [bug #87880] Properly unpack the incoming vector read data. - * Rework the handshake when making a parallel connection. Previous method - caused a deadlock when parallel connections were requested (e.g. xrdcp). - * Add HAVE_SENDFILE definition to cmake config. All post-cmake version of - xrootd until now have disabled use of sendfile() with resulting poor - performance. This fix corrects this. - * Don't force libXrdPss.so to be loaded for proxy managers. - * Fix various CMake issues: disable library inheritance, fix underlinking - problems, make sure libcom_err is present when building kerberos. - * Replace non-reentrant versions of getpwxxx and getgrxxx with reentrant - versions. This should prevent spurious uid/gid translations. - * Fix RedHat bug #673069: Missing header files required by DPM - * Don't ignore errors returned by kXR_close - * Init scripts: don't change the ownership of the sysconfig files - preventing the xrootd user from executing arbitrary code as root - -+ **Minor bug fixes** - * Add 'k' to the option list. It was wrongly deleted in the last option - refalgamization. - * Fix a typo in the specfile causing problems with multithreaded - compilation. - * Initialize xattr variable name so that xrdadler32 can fetch previous - checksum. The error caused xrdadler32 to always recompute the checksum. - * Make sure that monitor write length is really negative. - * Add the oss.asize hint to the destination URL in all possible cases. - * Properly print adler32 checksum in xrdcp. - * When the server certificate is expired, try to renew from the same path - before failing. - * Get the signing certificate for the CRL from its issuer hash, which can be - different from the CA hash. - * Add check for the format of the downloaded CRLs: DER or PEM - * Solaris init script: switch to xrootd user when invoked as root - * RHEL init scripts: always create /var/run/xrootd to handle /var/run - being mounted as tmpfs - -+ **Miscellaneous** - * Relax requirements on the permission mode of the x509 key files - * Disable client redirections reports to the console. - * Stop doing XrdFfsPosix_statall() if task queue is long. - * Get rid of compiler warnings - * Improve some log messages - * At server startup, only initialize the CA (and CRL, if required) for the - authority issuing the server certificate; additional CA's are initialized - only if needed. - -------------- -Version 3.1.0 -------------- - -+ **New Features** - * Use CMake to build the source code and retire all the other build systems. - * Add IOV as a selectable detail to xrootd.monitor directive. - * Provide a mode in xrootdfs to auto-update internal list of data servers. - and extend client connection TTL from one hour to infinity. - * Provide virtual xattr ("xroot.cksum") to obtain checksum for consistency. - * Make xrdadler32 use the new checksum format if it is set (fallback to old - format otherwise). In all cases, the old format is converted to the new - format whenever possible. - * Enforce r/o exports in the proxy server (finally added). - * Allow auto-fluching of I/O stream monitoring (default is off). - Patch submitted by Matevz Tadel, UCSD. - * Make proxy honor the export list at the storage layer. This allows sites - to disable staging via the proxy by specifying nostage for otherwise locally - stageable paths. - * Do not export the stage attribute to the meta-manager unless the path is - tagged with the stage+ attrbute on the export directive. - * WARNING: This update makes the oss plug-in source incompatible because an - additional parameter was added to the Stat() method. The update is binary - compatible and so only affects sites that recompile their plug-in. - * Allow the query checksum request to be issued via a proxy server. - * Add a query checksum interface to the POSIX interface. - * Defines the livXrdSecgsiAuthzVO plug-in to allow easy mapping from voms - vo names to users and groups. The plugin is configurable at run-time. - * Allow the OucErrInfo object to point to an environment. - * Add method to SysDNS to format an AF_INETx address into the RFC IPV6 - recommended format. - * Allow pointers to be placed in the OucEnv environment table. - * Extend the kXR_protocol request to allow the server to return detailed - information about node's role. This is backwardly compatible. - * The client uses kXR_protocol request to query for the server's role - (to distinguish managers from meta managers). - * The client goes back to a meta manager on authentication failure. - * The client prints to stdout the redirections it gets. This behavior may be - disabled by setting the XRD_PRINTREDIRECTS environment variable to 0, or, - from C++ by saying: EnvPutInt( NAME_PRINT_REDIRECTS, 0 ) - * Set $HOST value for possible copycmd substitution. - * Phase 1 to allow for redirection monitoring. Add rbuff and redir options - to the xrootd.monitor directive. - * Add error, redirect, and delay counts to the xrootd protocol summary - statistics. - * Allow file additions/deletion to be communicated to the XrdCnsd so that is - can maintain an accurate inventory. This update adds the frm.all.cnsd - directive which specifies how the information is to be commuincated. - * Enable cmsd monitoring. For now, only [meta]manager information is reported. - * Add new repstats config directive to increase reporting detail. - * New class, XrdCmsRole, to make role naming/handling consistent. - * Implement the 'cms.delay qdn' directive which allows one to tell the - meta-manager the minimum number of responses needed to satisfy a hold - delay (i.e. fast redirect). - * Accept XrdSecSSSKT envar as documented but also continue to support - XrdSecsssKT for backward compatibility. - * Allow servers to specify to the meta-manager what share of requests they - are willing to handle. Add the 'cms.sched gsdflt' and 'cms.sched gshr' - configuration directives to specify this. - * Include additional information in the protocol statistics. - * Resize some counters to prevent overflows. - * Add the 'cms.delay qdn' directive to allow better redirection control in - the future. - * Allow a plugin (notably the proxy plugin) to disable async I/O. - * Implement a general memory caching object. Currently, this will be used - by the Posix object. - * Allow optional memory caching when using the Posix library. This is - primarily used by the proxy object to reduce trips to a data server when - small blocks are accessed via the proxy server. This requires - configuration using the new 'pss.memcache' directive. - * Finally implement adding authentication information to the user monitoring - record (requested by Matevz Tadel, CMS). This adds a new generic option, - auth, to the xrootd.monitor directive. It needs to be specified for the - authentication information to be added. This keeps backward compatibility. - * Add a new method, chksum, to the standard filesystem interface. - * Integrate checksums into the logical filesystem layer implementation. - See the ofs.ckslib directive on how to do non-default configuration. - This also added a more effecient lfn2pfn() method to the storage system. - * Allow native checksums to be enabled in the xrootd layer. - See the xrootd.chksum directive on how to do this. - * Add checksum management to the frm_admin command. - * Allow XrdOucProg to dispatch a local program as well as a process. - * Allow a line to be insrerted into an XrdOucStream managed stream. - * Implement native checksums usable stand-alone or as plugins. Three digests - are supported: adler32, crc32, and md5. An additional digest can be added - via a plugin. Also, the native digests can be over-ridden via a plugin. - * In XrdSecgsi, new interface for the authorization plug-in which has now full - access to the XrdSecEntity object, with the possibility to fill/modify all the - fields according to the proxy chain. The plug-in is now called at the end of - the all process, after a successful handshake and DN-username mapping. - Implementations must contain three extern C functions; see the dummy example - provided in src/XrdSecgsi/XrdSecgsiAuthzFunDN.cc. - See also the header of XrdSecProtocolgsi::LoadAuthzFun. - * In XrdCryptosslgsiAux, add function to extract the VOMS attributes; can be - used in authz plug-ins. - * In XrdSecgsi, add possibility to extract the VOMS attributes and save them - in the XrdSecEntity. New switch '-vomsat:0/1 [1]'. - * In 'xrdgsiproxy info' show also the VOMS attributes, if present. - * Automatically build the RPM for the xrootd user when an OSG build is detected - and add fedora > 15 init scripts dependencies - -+ **Major bug fixes** - * Do not close the loger's shadow file descriptor when backgrounding as - this may cause random crashes later on. - * Avoid SEGV by setting network pointer prior to loading the 1st protocol. - * Enforce r/o path during mkdir operations. - * Avoid segv when initializing the finder on a multi-core machine. - * Fix incorrect lock handling for multiple waiters. - * Fix possible deadlocks in XrdSutCache preventing the pwd security module - to work correctly - -+ **Minor bug fixes** - * Properly handle the case when a site has an excessive number of groups - assignments. - * Prevent the response to a query from being truncated on the client side. - * Report readv information in the detailed monitoring stream. - * Correct default settings due to feature interactions after the fact. Now, - oss.defaults acts as if the setting were actually specified via oss.export. - * Actually use the N2N library of specified or implied via pss.localroot - for proxy server interactions withthe origin (required for Atlas T2). - * Use re-enterant versions of getpwuid() and getpwgid(). This is need for - FUSE. - * Correct bad english in a few error messages. - * Set correct checksum length when converting ASCII to binary. - * Allow the sss protocol to work for multi-homed hosts. - * Correct definition of AtomicISM that caused the maximum link count to - never be updated in the statistics. - * Apply N2N mapping to source path when relocating the file. - * Report correct port when locate is directly issued to a data server - (before it was being reported as 0). - * Make the default file system a pointer to a dynamic instance of XrdOfs - instead of a global static (i.e. the Andreas Peters patch). This makes - writing an ofs plugin easier. - * Fix the RPM uninstall scriptlets incorrectly invoking /sbin/ldconfig. - * Install XrdOlbMonPerf and netchk tools. - * Fix a bug preventing the core of authentication errors to be logged to clients - * In the krb5 security plugin, define KRB5CCNAME to point to the credential - cache file /tmp/krb5cc_ only if this file exists and is readable. - Solves an issue with credentials cached in memory (API::n). - * Fix array deletion mismatches reported by cppcheck (from D. Volgyes) - * Make sure that loading of XrdSecgsi.so fails if either the GMAPFun or the - AuthzFun plug-ins fail to load. - -+ **Miscellaneous** - * Drop Windows support. - * Code cleanup: remove XrdTokenAuthzOfs, simple tests, broken utilities, - the gridftp code, krb4 and secssl plugins, obsolete documentation files - * Make the loadable module extensions configurable depending on the platform - (so on Linux and Solaris, dylib on MacOs) - * Add new XrdVNUMBER macro. - * Clean up the conditional compilation macros. - * Remove compression related attributes (compchk, ssdec) and directives - (compdetect) as they were never used nor fully implemented. - * Remove the userprty directive. It was deprecated and never specified. - * Refactor PosixPreeload and Posix libraries to prevent split initialization - of the preload library which will cause failures on certain systems. - * Provide automatic proxy checksum defaults when role is set to proxy. - * Remove all references via extern statements to object instances. This - only applies to the Xrd package. - * Do not echo lines qualified by an in-line if when the if fails. - * Remove the old "redirect" directive. It has passed its prime. - * Remove back references to symbols defined in XrdXrootd package used by - the cms client to allow for clean shared library builds. - * Remove externs to XrdSecGetProtocol and XrdSecGetService from - XrdSecInterface.hh to avoid having undefined references just because the - include file was included somewhere. - * Rename XrdNetDNS to XrdSysDNS to avoid cross-dependencies. This means that all - plug-in developers will need to do the same as XrdNetDNS no longer exists. - * Split XrdFrm into XrdFrm and XrdFrc. This prevents cross-dependencies in - packages that use the File Residency Manager. - -------------- -Version 3.0.5 -------------- - -+ **Major bug fixes** - * Avoid stage failures when target file exists in purgeable or writable space. - * Make sure all the threads are joined when closing a physical connection. - * Fix free/delete mismatch in XrdSecProtocolgsi et al. - -+ **Minor bug fixes** - * Remove old async shutdown workaround patch introduced in Linux 2.3. The - problem has been since fixed and the solution now causes problems. - * Install the netchk tool - -------------- -Version 3.0.4 -------------- - -+ **New features** - * xrdcp now has -version parameter - * xrdcp automatically ads the oss.asize hint to the url opaque data. - This functionality may be disabled by setting the XrdCpSizeHint - variable to 0 (XRD_XRDCPSIZEHIN in the shell). - * The client will try to resolve the server hostname on every retry to - enable DNS failovers. - * RPM: devel package split into libs-devel, client-devel and server-devel - * XrootdFS: all paramenters can be passed via command line, add -h. - * Allow a plugin (notably the proxy plugin) to disable async I/O. - * New class XrdSysRWLock interfacing the pthread_rwlock functionality - * In XrdSecEntity: Add new fields 'creds' and 'credslen' to be filled - with the raw client credentials - * In XrdSutCache: Use XrdSysRWLock to coordinate concurrent access to - the cache - * In XrdSecgsi: - - - Add option to have Entity.name filled with the plain DN, instead of - the DN hash, when no mapping is requested or found. - - - Enable cache also for authz mapping results. - - - Do not require the existence of a grid-mapfile if gmapopt=2 and there is at least - a gmapfun or an authzfun defined. - - - Add example of mapping function allowing to match parts of the DN - - - Extend existing option 'authzpxy' to allow exporting the incoming client credentials in - XrdSecEntity. - -+ **Major bug fixes** - * Async write errors are now being properly caught and reacted to. - XrdClient::Close will now fail if it cannot recover from async - write errors. - * xrdcp prints an error message and returns failure to the shell - when some of the write requests it issues fail. - * libXrdPosixPreload now builds with autotools and is included into - the xrootd-client RPM - * RPM: FFS moved from libs to client - * Properly parse oss.asize. This because a major problem when xrdcp started - adding this to the url which causes the copy to fail. - * Spin connection portion of proxy initialization to a background thread. - This prevents init.d hangs when a redirector is not available. - -+ **Minor bug fixes** - * Test for 64-bit atomics instead 32-bit ones. Fixes build on 32-bit PowerPC. - * RPM: xrootd-fuse now depends on fuse - * Take correctly into accoutn summer time in calculating the time left for - a proxy validity - * Add support for Ubuntu 11 which uses the directory /usr/lib/`dpkg-architecture - -qDEB_HOST_MULTIARCH` to store platform dependent libraries. - -------------- -Version 3.0.3 -------------- - -+ **New features** - * Change configure.classic to handle various versions of processors in a - more sane way (this fixes several Solaris issues and atomics for i686). - * Add fwdwait option to cms.request directive to allow pacing of forwarded - requests (off by default). - * Use native memory synchronization primitives when available when doing - network I/O. This will eventually be extended to cover all other cases. - * Add the qdl option to the cms.delay directive to allow changing the - query window independently of the time a client is asked to wait for the - query to actually complete. - * Add 'pss.namelib' directive to allow proxies to pre-translate the lfn - for servers that cannot do so (e.g., dCache xrootd door). - * Optimize handling of shared-everything ile systems (e.g., dCache, GPFS, - DFS, Lustre, etc.) in the cmsd. - * Implement optional throttling for meta-manager requests in the cmsd. - * New cmsd directive, cms.dfs, declares that the underlying file system - is a shared-everything system (i.e., distributed file system) and allow - for optimal configuration and meta-manager throttling. - * Change the oss and fm components to use file extended attributes instead - of meta-files. This affects copy, create, reloc, rename, and unlink in the - oss layer. Migrate, purge, transfer, and most admin commands in the frm - component. The 'all.export' directive now accepts the noxattr/xattr option. - WARNING: If the migrator or purge options have been specified for any path - in the 'all.export; directive then this change requires either the the - 'oss.runmodeold' directive be added to the configuration file to provide - backward compatibility or that the name and data spaces be migrated using - the frm_admin command. See "Migrating tp Extended Attributes" manual for - detailed information and the new 'frm_admin convert' subcommand. - * Avoid physical copy if the operation can be handled using hard links. This - greatly speeds up static space token reassignment. - * Add platform independent interface to extended file attributes. - * RPM packaging and Red Hat Enterprise Linux compatible init scripts - capable of handling multiple instances of the xrootd daemons. The instances - can be defined in the /etc/sysconfig/xrootd file and then handled using standard:: - - service xrootd start|stop|... - service cmsd start|stop|... - ... - - or handled by name:: - - service xrootd start instance1 instance5 - - * New '-s' commandline option for xrootd, cmsd, frm_purged and frm_xfrd - creating a pidfile. - * xrootd, cmsd, frm_purged and frm_xfrd now return failure to the shell - when called with '-b' option (daemonization) and the daemon fails to - initialize. - * New 'EnableTCPKeepAlive' client environment option added enabling the TCP - stack keep-alive functionality for the sockets. - On Linux three addtional fine-tunning options are available: - - - TCPKeepAliveTime - interval (in seconds) between the last data packet and the first keep-alive - probe - - TCPKeepAliveInterval - interval (in seconds) between the probes - - TCPKeepAliveProbes - number of probes lost to consider the connection broken - - * New functionality handling process forking. When enabled (via the 'EnableForkHandlers' - env option) prior to a call to fork it shuts down all the xrootd connection management - facilities (including the connections themselves) and reinitializes them after the fork - both in the parent and the child process. This ensures relative fork safety provided - that all the XrdClient and XrdAdmin instances are closed when the fork function is invoked. - -+ **Major bug fixes** - * Add missing braces that caused config failure in frm_admin command. - * Account for correct path when -M value is zero in hpsscp command. - * In XrdCryptossl, fix for thread-safeness; solves random crashes observed on the - server side under high authentication frequency - * In XrdOucBonjour, fix important issue with host domain name registration, preventing - the correct domain to be posted. - -+ **Minor bug fixes** - * Correct file discovery propogation for proxy manager relative to meta-managers. - * Correct oss partition selection algorithm to further spread out file - allocation. - * Allow underscores in set/setenv variables. - * Add null byte after checksum value response. - * Move mapping of errno to xrootd error code to the protocol package where it - belongs. This also removes a cross dependency. - * Correct RetToken() behaviour in the presence of multiple spaces between tokens and - the previous call returned the remainder of the line (very obscure circumstances). - * [bug #77535] xrdcp now returns an error to the shell when it fails to copy the file - * [bug #79710] xrdcp now gracefully aborts when it encounters a corrupted local file - * Reset the transaction timeout for the Query method. - This fixes transaction timeout issues for clients doing only queries. - * Rename variable to remove conflict between it and global of the same name. - * Fix frm_admin command line option parsing so it does not trip over - subcommand options. This also fixes a SEGV in MacOS when this actually - happens. - * Enable the '-md5' option when OpenSSL is present and xrootd is built with autotools. - -+ **Documentation** - * Added man pages for: xprep, xrd, xrdcp, xrdstagetool, xrdgsiproxy - -------------- -Version 3.0.2 -------------- - -+ **Minor bug fixes** - * Fix the build on Solaris 10. - * Fix the build on SLC4. - * Fix the out-of-the-source-tree builds with autotools. - * Fix a segfault while doing a recursive copy from root:// to root://. - -------------- -Version 3.0.1 -------------- - -+ **New features** - * New application, cconfing, added to display configuration files relative to a host-program-instance. - * New application, netchk, that tests that firewalls have been correctly setup. - * New configure.classic option to allow use of stl4port library for Solaris. - * New internal feature in XrdPosix library to not shadow files with actual file descriptors (used by the proxy - service). This increases scalability. - * Allow the xrootd server to tell the client that it is a meta-manager. - * Support fo proxies generated by Globus version 4.2.1 in libXrdSecssl. - -+ **Major bug fixes** - * Change link options for xrdadler32 to not use shared libraries. The previous setup caused the command to hang - upon exit. - * Remove instance of XrdPosixXrootd from that same file. Including it disallows defaults from being changed. - -+ **Minor bug fixes** - * Fix XrdOucStream to not return ending "fi". - * Correct network option interference -- do not turn on network nodnr option should the keepalive option - be specified. - * Remove duplicate option in option table used by the proxy service. - * Compile on Solaris 11 Express using SunCC. - * Compile on Windows using MSVC++2010. diff --git a/src/XrdCeph/packaging/debian/compat b/src/XrdCeph/packaging/debian/compat deleted file mode 100644 index f599e28b8ab..00000000000 --- a/src/XrdCeph/packaging/debian/compat +++ /dev/null @@ -1 +0,0 @@ -10 diff --git a/src/XrdCeph/packaging/debian/control b/src/XrdCeph/packaging/debian/control deleted file mode 100644 index 86b8341286f..00000000000 --- a/src/XrdCeph/packaging/debian/control +++ /dev/null @@ -1,48 +0,0 @@ -Source: xrootd -Maintainer: Jozsef Makai -Section: misc -Priority: optional -Standards-Version: 3.9.3 -Build-Depends: debhelper (>= 9), cmake (>=3.3.0), zlib1g-dev, libfuse-dev, python-dev, libssl-dev, libxml2-dev, ncurses-dev, libkrb5-dev, libreadline-dev, libsystemd-dev, selinux-policy-dev, systemd -Homepage: https://github.com/xrootd/xrootd -Vcs-Git: https://github.com/xrootd/xrootd.git -Vcs-Browser: https://github.com/xrootd/xrootd - -Package: xrootd-libs -Architecture: any -Description: This package contains libraries used by the xrootd servers and clients. - -Package: xrootd-devel -Architecture: any -Depends: ${shlibs:Depends}, xrootd-libs (=${binary:Version}) -Description: This package contains header files and development libraries for xrootd development. - -Package: xrootd-client-libs -Architecture: any -Depends: ${shlibs:Depends}, xrootd-libs (=${binary:Version}) -Description: This package contains libraries used by xrootd clients. - -Package: xrootd-client-devel -Architecture: any -Depends: ${shlibs:Depends}, xrootd-devel (=${binary:Version}), xrootd-client-libs (=${binary:Version}) -Description: This package contains header files and development libraries for xrootd client development. - -Package: xrootd-client -Architecture: any -Depends: ${shlibs:Depends}, libxml2, xrootd-libs (=${binary:Version}), xrootd-client-libs (=${binary:Version}) -Description: This package contains the command line tools used to communicate with xrootd servers. - -Package: xrootd-private-devel -Architecture: any -Depends: ${shlibs:Depends}, xrootd-libs (=${binary:Version}) -Description: This package contains some private xrootd headers. The use of these headers is strongly discouraged. Backward compatibility between versions is not guaranteed for these headers. - -Package: xrootd-server-libs -Architecture: any -Depends: ${shlibs:Depends}, xrootd-libs (=${binary:Version}), xrootd-client-libs (=${binary:Version}) -Description: This package contains libraries used by xrootd servers. - -Package: xrootd-server-devel -Architecture: any -Depends: ${shlibs:Depends}, xrootd-devel (=${binary:Version}), xrootd-client-devel (=${binary:Version}), xrootd-server-libs (=${binary:Version}) -Description: This package contains header files and development libraries for xrootd server development. diff --git a/src/XrdCeph/packaging/debian/copyright b/src/XrdCeph/packaging/debian/copyright deleted file mode 100644 index 8fd5621be2c..00000000000 --- a/src/XrdCeph/packaging/debian/copyright +++ /dev/null @@ -1,18 +0,0 @@ -"Copyright (c) 2005-2012, Board of Trustees of the Leland Stanford, Jr. University.\n" -"Produced under contract DE-AC02-76-SF00515 with the US Department of Energy. \n" -"All rights reserved. The copyright holder's institutional names may not be used to\n" -"endorse or promote products derived from this software without specific prior \n" -"written permission.\n\n" -"This file is part of the XRootD software suite. \n\n" -"XRootD is free software: you can redistribute it and/or modify it under the terms \n" -"of the GNU Lesser General Public License as published by the Free Software \n" -"Foundation, either version 3 of the License, or (at your option) any later version.\n\n" -"XRootD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n" -"without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR \n" -"PURPOSE. See the GNU Lesser General Public License for more details. \nn" -"You should have received a copy of the GNU Lesser General Public License along \n" -"with XRootD in a file called COPYING.LESSER (LGPL license) and file COPYING (GPL \n" -"license). If not, see .\n\n" -"Prior to September 2nd, 2012 the XRootD software suite was licensed under a\n" -"modified BSD license (see file COPYING.BSD). This applies to all code that\n" -"was in the XRootD git repository prior to that date.\n" diff --git a/src/XrdCeph/packaging/debian/rules b/src/XrdCeph/packaging/debian/rules deleted file mode 100755 index a89c9970c54..00000000000 --- a/src/XrdCeph/packaging/debian/rules +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/make -f - -%: - dh $@ --builddirectory=build --destdir=deb_packages - -override_dh_auto_configure: - dh_auto_configure -- -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_LIBDIR=lib/$(shell dpkg-architecture -qDEB_HOST_MULTIARCH) - -override_dh_install: - install -D -m 644 packaging/common/client.conf deb_packages/etc/xrootd/client.conf - install -D -m 644 packaging/common/client-plugin.conf.example deb_packages/etc/xrootd/client.plugins.d/client-plugin.conf.example - dh_install --sourcedir=deb_packages diff --git a/src/XrdCeph/packaging/debian/source/format b/src/XrdCeph/packaging/debian/source/format deleted file mode 100644 index 163aaf8d82b..00000000000 --- a/src/XrdCeph/packaging/debian/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (quilt) diff --git a/src/XrdCeph/packaging/debian/xrootd-client-devel.install b/src/XrdCeph/packaging/debian/xrootd-client-devel.install deleted file mode 100644 index 321cbc0430c..00000000000 --- a/src/XrdCeph/packaging/debian/xrootd-client-devel.install +++ /dev/null @@ -1,9 +0,0 @@ -usr/bin/xrdgsitest -usr/lib/*/libXrdCl.so -usr/lib/*/libXrdClient.so -usr/lib/*/libXrdFfs.so -usr/lib/*/libXrdPosix.so -usr/share/man/man1/xrdgsitest.1* -usr/include/xrootd/XrdCl -usr/include/xrootd/XrdClient -usr/include/xrootd/XrdPosix diff --git a/src/XrdCeph/packaging/debian/xrootd-client-libs.install b/src/XrdCeph/packaging/debian/xrootd-client-libs.install deleted file mode 100644 index a8a45bfa66c..00000000000 --- a/src/XrdCeph/packaging/debian/xrootd-client-libs.install +++ /dev/null @@ -1,8 +0,0 @@ -usr/lib/*/libXrdCl.so.2* -usr/lib/*/libXrdClient.so.2* -usr/lib/*/libXrdFfs.so.2* -usr/lib/*/libXrdPosix.so.2* -usr/lib/*/libXrdPosixPreload.so.1* -usr/lib/*/libXrdPosixPreload.so -etc/xrootd/client.plugins.d/client-plugin.conf.example -etc/xrootd/client.conf diff --git a/src/XrdCeph/packaging/debian/xrootd-client-libs.postinst b/src/XrdCeph/packaging/debian/xrootd-client-libs.postinst deleted file mode 100644 index 2983b531b1f..00000000000 --- a/src/XrdCeph/packaging/debian/xrootd-client-libs.postinst +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -ldconfig diff --git a/src/XrdCeph/packaging/debian/xrootd-client-libs.postrm b/src/XrdCeph/packaging/debian/xrootd-client-libs.postrm deleted file mode 100644 index 2983b531b1f..00000000000 --- a/src/XrdCeph/packaging/debian/xrootd-client-libs.postrm +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -ldconfig diff --git a/src/XrdCeph/packaging/debian/xrootd-client.install b/src/XrdCeph/packaging/debian/xrootd-client.install deleted file mode 100644 index d4ca23ae269..00000000000 --- a/src/XrdCeph/packaging/debian/xrootd-client.install +++ /dev/null @@ -1,18 +0,0 @@ -usr/bin/xprep -usr/bin/xrd -usr/bin/xrdadler32 -usr/bin/xrdcopy -usr/bin/xrdcp -usr/bin/xrdcp-old -usr/bin/xrdfs -usr/bin/xrdgsiproxy -usr/bin/xrdstagetool -usr/share/man/man1/xprep.1* -usr/share/man/man1/xrd.1* -usr/share/man/man1/xrdadler32.1* -usr/share/man/man1/xrdcopy.1* -usr/share/man/man1/xrdcp.1* -usr/share/man/man1/xrdcp-old.1* -usr/share/man/man1/xrdfs.1* -usr/share/man/man1/xrdgsiproxy.1* -usr/share/man/man1/xrdstagetool.1* diff --git a/src/XrdCeph/packaging/debian/xrootd-devel.install b/src/XrdCeph/packaging/debian/xrootd-devel.install deleted file mode 100644 index 8ce0c9c9a9e..00000000000 --- a/src/XrdCeph/packaging/debian/xrootd-devel.install +++ /dev/null @@ -1,15 +0,0 @@ -usr/bin/xrootd-config -usr/include/xrootd/XProtocol -usr/include/xrootd/Xrd -usr/include/xrootd/XrdCks -usr/include/xrootd/XrdNet -usr/include/xrootd/XrdOuc -usr/include/xrootd/XrdSec -usr/include/xrootd/XrdSys -usr/include/xrootd/XrdVersion.hh -usr/include/xrootd/XrdXml/XrdXmlReader.hh -usr/lib/*/libXrdAppUtils.so -usr/lib/*/libXrdCrypto.so -usr/lib/*/libXrdCryptoLite.so -usr/lib/*/libXrdUtils.so -usr/lib/*/libXrdXml.so diff --git a/src/XrdCeph/packaging/debian/xrootd-libs.install b/src/XrdCeph/packaging/debian/xrootd-libs.install deleted file mode 100644 index 8faf68a3046..00000000000 --- a/src/XrdCeph/packaging/debian/xrootd-libs.install +++ /dev/null @@ -1,9 +0,0 @@ -usr/lib/*/libXrdAppUtils.so.1* -usr/lib/*/libXrdClProxyPlugin-4.so -usr/lib/*/libXrdCks*-4.so -usr/lib/*/libXrdCrypto.so.1* -usr/lib/*/libXrdCryptoLite.so.1* -usr/lib/*/libXrdCryptossl-4.so -usr/lib/*/libXrdSec*-4.so -usr/lib/*/libXrdUtils.so.* -usr/lib/*/libXrdXml.so.* diff --git a/src/XrdCeph/packaging/debian/xrootd-libs.postinst b/src/XrdCeph/packaging/debian/xrootd-libs.postinst deleted file mode 100644 index 2983b531b1f..00000000000 --- a/src/XrdCeph/packaging/debian/xrootd-libs.postinst +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -ldconfig diff --git a/src/XrdCeph/packaging/debian/xrootd-libs.postrm b/src/XrdCeph/packaging/debian/xrootd-libs.postrm deleted file mode 100644 index 2983b531b1f..00000000000 --- a/src/XrdCeph/packaging/debian/xrootd-libs.postrm +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -ldconfig diff --git a/src/XrdCeph/packaging/debian/xrootd-private-devel.install b/src/XrdCeph/packaging/debian/xrootd-private-devel.install deleted file mode 100644 index 38c034f2bb2..00000000000 --- a/src/XrdCeph/packaging/debian/xrootd-private-devel.install +++ /dev/null @@ -1,3 +0,0 @@ -usr/include/xrootd/private -usr/lib/*/libXrdSsiLib.so -usr/lib/*/libXrdSsiShMap.so diff --git a/src/XrdCeph/packaging/debian/xrootd-server-devel.install b/src/XrdCeph/packaging/debian/xrootd-server-devel.install deleted file mode 100644 index d1dcf33b6c5..00000000000 --- a/src/XrdCeph/packaging/debian/xrootd-server-devel.install +++ /dev/null @@ -1,8 +0,0 @@ -usr/include/xrootd/XrdAcc -usr/include/xrootd/XrdCms -usr/include/xrootd/XrdFileCache -usr/include/xrootd/XrdOss -usr/include/xrootd/XrdSfs -usr/include/xrootd/XrdXrootd -usr/include/xrootd/XrdHttp -usr/lib/*/libXrdServer.so diff --git a/src/XrdCeph/packaging/debian/xrootd-server-libs.install b/src/XrdCeph/packaging/debian/xrootd-server-libs.install deleted file mode 100644 index de9c8be9e28..00000000000 --- a/src/XrdCeph/packaging/debian/xrootd-server-libs.install +++ /dev/null @@ -1,14 +0,0 @@ -usr/lib/*/libXrdBwm-4.so -usr/lib/*/libXrdPss-4.so -usr/lib/*/libXrdXrootd-4.so -usr/lib/*/libXrdFileCache-4.so -usr/lib/*/libXrdBlacklistDecision-4.so -usr/lib/*/libXrdHttp-4.so -usr/lib/*/libXrdN2No2p-4.so -usr/lib/*/libXrdOssSIgpfsT-4.so -usr/lib/*/libXrdServer.so.* -usr/lib/*/libXrdSsi-4.so -usr/lib/*/libXrdSsiLib.so.* -usr/lib/*/libXrdSsiLog-4.so -usr/lib/*/libXrdSsiShMap.so.* -usr/lib/*/libXrdThrottle-4.so diff --git a/src/XrdCeph/packaging/debian/xrootd-server-libs.postinst b/src/XrdCeph/packaging/debian/xrootd-server-libs.postinst deleted file mode 100644 index 2983b531b1f..00000000000 --- a/src/XrdCeph/packaging/debian/xrootd-server-libs.postinst +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -ldconfig diff --git a/src/XrdCeph/packaging/debian/xrootd-server-libs.postrm b/src/XrdCeph/packaging/debian/xrootd-server-libs.postrm deleted file mode 100644 index 2983b531b1f..00000000000 --- a/src/XrdCeph/packaging/debian/xrootd-server-libs.postrm +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -ldconfig diff --git a/src/XrdCeph/packaging/debian_scripts/publish_debian_cern.sh b/src/XrdCeph/packaging/debian_scripts/publish_debian_cern.sh deleted file mode 100755 index 204914cec25..00000000000 --- a/src/XrdCeph/packaging/debian_scripts/publish_debian_cern.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -#------------------------------------------------------------------------------- -# Publish debian artifacts on CERN Gitlab CI -# Author: Jozsef Makai (11.08.2017) -#------------------------------------------------------------------------------- - -set -e - -comp=$1 -prefix=/eos/project/s/storage-ci/www/debian/xrootd - -for dist in artful xenial; do - echo "Publishing for $dist"; - path=$prefix/pool/$dist/$comp/x/xrootd/; - mkdir -p $path; - if [[ "$comp" == "master" ]]; then find ${path} -type f -name '*deb' -delete; fi - cp $dist/*deb $path; - mkdir -p $prefix/dists/$dist/$comp/binary-amd64/; - (cd $prefix && apt-ftparchive --arch amd64 packages pool/$dist/$comp/ > dists/$dist/$comp/binary-amd64/Packages); - gzip -c $prefix/dists/$dist/$comp/binary-amd64/Packages > $prefix/dists/$dist/$comp/binary-amd64/Packages.gz; - components=$(find $prefix/dists/$dist/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | tr '\n' ' ') - if [ -e $prefix/dists/$dist/Release ]; then - rm $prefix/dists/$dist/Release - fi - if [ -e $prefix/dists/$dist/InRelease ]; then - rm $prefix/dists/$dist/InRelease - fi - if [ -e $prefix/dists/$dist/Release.gpg ]; then - rm $prefix/dists/$dist/Release.gpg - fi - apt-ftparchive -o APT::FTPArchive::Release::Origin=CERN -o APT::FTPArchive::Release::Label=XrootD -o APT::FTPArchive::Release::Codename=artful -o APT::FTPArchive::Release::Architectures=amd64 -o APT::FTPArchive::Release::Components="$components" release $prefix/dists/$dist/ > $prefix/dists/$dist/Release; - gpg --homedir /home/stci/ --clearsign -o $prefix/dists/$dist/InRelease $prefix/dists/$dist/Release; - gpg --homedir /home/stci/ -abs -o $prefix/dists/$dist/Release.gpg $prefix/dists/$dist/Release; -done diff --git a/src/XrdCeph/packaging/makesrpm.sh b/src/XrdCeph/packaging/makesrpm.sh deleted file mode 100755 index b74b90fd471..00000000000 --- a/src/XrdCeph/packaging/makesrpm.sh +++ /dev/null @@ -1,256 +0,0 @@ -#!/bin/bash -#------------------------------------------------------------------------------- -# Create a source RPM package -# Author: Lukasz Janyst (10.03.2011) -#------------------------------------------------------------------------------- - -RCEXP='^[0-9]+\.[0-9]+\.[0-9]+\-rc.*$' -CERNEXP='^[0-9]+\.[0-9]+\.[0-9]+\-[0-9]+\.CERN.*$' - -#------------------------------------------------------------------------------- -# Find a program -#------------------------------------------------------------------------------- -function findProg() -{ - for prog in $@; do - if test -x "`which $prog 2>/dev/null`"; then - echo $prog - break - fi - done -} - -#------------------------------------------------------------------------------- -# Print help -#------------------------------------------------------------------------------- -function printHelp() -{ - echo "Usage:" 1>&2 - echo "${0} [--help] [--source PATH] [--output PATH]" 1>&2 - echo " --help prints this message" 1>&2 - echo " --source PATH specify the root of the source tree" 1>&2 - echo " defaults to ../" 1>&2 - echo " --output PATH the directory where the source rpm" 1>&2 - echo " should be stored, defaulting to ." 1>&2 - echo " --version VERSION the version provided by user" 1>&2 - echo " --define 'MACRO EXPR'" 1>&2 -} - -#------------------------------------------------------------------------------- -# Parse the commandline, if only we could use getopt... :( -#------------------------------------------------------------------------------- -SOURCEPATH="../" -OUTPUTPATH="." -PRINTHELP=0 - -while test ${#} -ne 0; do - if test x${1} = x--help; then - PRINTHELP=1 - elif test x${1} = x--source; then - if test ${#} -lt 2; then - echo "--source parameter needs an argument" 1>&2 - exit 1 - fi - SOURCEPATH=${2} - shift - elif test x${1} = x--output; then - if test ${#} -lt 2; then - echo "--output parameter needs an argument" 1>&2 - exit 1 - fi - OUTPUTPATH=${2} - shift - elif test x${1} = x--version; then - if test ${#} -lt 2; then - echo "--version parameter needs an argument" 1>&2 - exit 1 - fi - USER_VERSION="--version ${2}" - shift - elif test x${1} = x--define; then - if test ${#} -lt 2; then - echo "--define parameter needs an argument" 1>&2 - exit 1 - fi - USER_DEFINE="$USER_DEFINE --define \""${2}"\"" - shift - fi - shift -done - -if test $PRINTHELP -eq 1; then - printHelp - exit 0 -fi - -echo "[i] Working on: $SOURCEPATH" -echo "[i] Storing the output to: $OUTPUTPATH" - -#------------------------------------------------------------------------------- -# Check if the source and the output dirs -#------------------------------------------------------------------------------- -if test ! -d $SOURCEPATH -o ! -r $SOURCEPATH; then - echo "[!] Source path does not exist or is not readable" 1>&2 - exit 2 -fi - -if test ! -d $OUTPUTPATH -o ! -w $OUTPUTPATH; then - echo "[!] Output path does not exist or is not writeable" 1>&2 - exit 2 -fi - -#------------------------------------------------------------------------------- -# Check if we have all the necassary components -#------------------------------------------------------------------------------- -if test x`findProg rpmbuild` = x; then - echo "[!] Unable to find rpmbuild, aborting..." 1>&2 - exit 1 -fi - -if test x`findProg git` = x; then - echo "[!] Unable to find git, aborting..." 1>&2 - exit 1 -fi - -#------------------------------------------------------------------------------- -# Check if the source is a git repository -#------------------------------------------------------------------------------- -if test ! -d $SOURCEPATH/.git; then - echo "[!] I can only work with a git repository" 1>&2 - exit 2 -fi - -#------------------------------------------------------------------------------- -# Check the version number -#------------------------------------------------------------------------------- -if test ! -x $SOURCEPATH/genversion.sh; then - echo "[!] Unable to find the genversion script" 1>&2 - exit 3 -fi - -VERSION=`$SOURCEPATH/genversion.sh --print-only $USER_VERSION $SOURCEPATH 2>/dev/null` -if test $? -ne 0; then - echo "[!] Unable to figure out the version number" 1>&2 - exit 4 -fi - -echo "[i] Working with version: $VERSION" - -if test x${VERSION:0:1} = x"v"; then - VERSION=${VERSION:1} -fi - -#------------------------------------------------------------------------------- -# Deal with release candidates -#------------------------------------------------------------------------------- -RELEASE=1 -if test x`echo $VERSION | egrep $RCEXP` != x; then - RELEASE=0.`echo $VERSION | sed 's/.*-rc/rc/'` - VERSION=`echo $VERSION | sed 's/-rc.*//'` -fi - -#------------------------------------------------------------------------------- -# Deal with CERN releases -#------------------------------------------------------------------------------- -if test x`echo $VERSION | egrep $CERNEXP` != x; then - RELEASE=`echo $VERSION | sed 's/.*-//'` - VERSION=`echo $VERSION | sed 's/-.*\.CERN//'` -fi - -#------------------------------------------------------------------------------- -# In case of user version check if the release number has been provided -#------------------------------------------------------------------------------- -if test x"$USER_VERSION" != x; then - TMP=`echo $VERSION | sed 's#.*-##g'` - if test $TMP != $VERSION; then - RELEASE=$TMP - VERSION=`echo $VERSION | sed 's#-[^-]*$##'` - fi -fi - -VERSION=`echo $VERSION | sed 's/-/./g'` -echo "[i] RPM compliant version: $VERSION-$RELEASE" - -#------------------------------------------------------------------------------- -# Create a tempdir and copy the files there -#------------------------------------------------------------------------------- -# exit on any error -set -e - -TEMPDIR=`mktemp -d /tmp/xrootd-ceph.srpm.XXXXXXXXXX` -RPMSOURCES=$TEMPDIR/rpmbuild/SOURCES -mkdir -p $RPMSOURCES -mkdir -p $TEMPDIR/rpmbuild/SRPMS - -echo "[i] Working in: $TEMPDIR" 1>&2 - -if test -d rhel -a -r rhel; then - for i in rhel/*; do - cp $i $RPMSOURCES - done -fi - -if test -d common -a -r common; then - for i in common/*; do - cp $i $RPMSOURCES - done -fi - -#------------------------------------------------------------------------------- -# Generate the spec file -#------------------------------------------------------------------------------- -if test ! -r rhel/xrootd-ceph.spec.in; then - echo "[!] The specfile template does not exist!" 1>&2 - exit 7 -fi -cat rhel/xrootd-ceph.spec.in | sed "s/__VERSION__/$VERSION/" | \ - sed "s/__RELEASE__/$RELEASE/" > $TEMPDIR/xrootd-ceph.spec - -#------------------------------------------------------------------------------- -# Make a tarball of the latest commit on the branch -#------------------------------------------------------------------------------- -# no more exiting on error -set +e - -CWD=$PWD -cd $SOURCEPATH -COMMIT=`git log --pretty=format:"%H" -1` - -if test $? -ne 0; then - echo "[!] Unable to figure out the git commit hash" 1>&2 - exit 5 -fi - -git archive --prefix=xrootd-ceph/ --format=tar $COMMIT | gzip -9fn > \ - $RPMSOURCES/xrootd-ceph.tar.gz - -if test $? -ne 0; then - echo "[!] Unable to create the source tarball" 1>&2 - exit 6 -fi - -cd $CWD - -#------------------------------------------------------------------------------- -# Build the source RPM -#------------------------------------------------------------------------------- -echo "[i] Creating the source RPM..." - -# Dirty, dirty hack! -echo "%_sourcedir $RPMSOURCES" >> $TEMPDIR/rpmmacros -eval "rpmbuild --define \"_topdir $TEMPDIR/rpmbuild\" \ - --define \"%_sourcedir $RPMSOURCES\" \ - --define \"%_srcrpmdir %{_topdir}/SRPMS\" \ - --define \"_source_filedigest_algorithm md5\" \ - --define \"_binary_filedigest_algorithm md5\" \ - ${USER_DEFINE} \ - -bs $TEMPDIR/xrootd-ceph.spec > $TEMPDIR/log" -if test $? -ne 0; then - echo "[!] RPM creation failed" 1>&2 - exit 8 -fi - -cp $TEMPDIR/rpmbuild/SRPMS/xrootd-ceph*.src.rpm $OUTPUTPATH -rm -rf $TEMPDIR - -echo "[i] Done." diff --git a/src/XrdCeph/packaging/rhel/xrootd-ceph.spec.in b/src/XrdCeph/packaging/rhel/xrootd-ceph.spec.in deleted file mode 100644 index 03b1caccebd..00000000000 --- a/src/XrdCeph/packaging/rhel/xrootd-ceph.spec.in +++ /dev/null @@ -1,167 +0,0 @@ -#------------------------------------------------------------------------------- -# Helper macros -#------------------------------------------------------------------------------- -%if %{?rhel:1}%{!?rhel:0} - %if %{rhel} >= 7 - %define use_systemd 1 - %else - %define use_systemd 0 - %endif -%else - %if %{?fedora}%{!?fedora:0} >= 19 - %define use_systemd 1 - %else - %define use_systemd 0 - %endif -%endif - -%if %{?fedora}%{!?fedora:0} >= 22 - %define use_libc_semaphore 1 -%else - %define use_libc_semaphore 0 -%endif - -%if %{?_with_ceph11:1}%{!?_with_ceph11:0} - %define _with_ceph 1 -%endif - -%if %{?rhel:1}%{!?rhel:0} - %if %{rhel} > 7 - %define use_cmake3 0 - %else - %define use_cmake3 1 - %endif -%else - %define use_cmake3 0 -%endif - -#------------------------------------------------------------------------------- -# Package definitions -#------------------------------------------------------------------------------- -Name: xrootd-ceph -Epoch: 1 -Version: __VERSION__ -Release: __RELEASE__%{?dist}%{?_with_clang:.clang} -Summary: CEPH plug-in for XRootD -Group: System Environment/Daemons -License: LGPLv3+ -URL: http://xrootd.org/ - -# git clone http://xrootd.org/repo/xrootd.git xrootd -# cd xrootd -# git-archive master | gzip -9 > ~/rpmbuild/SOURCES/xrootd.tgz -Source0: xrootd-ceph.tar.gz - -BuildRoot: %{_tmppath}/%{name}-root - -%if %{use_cmake3} -BuildRequires: cmake3 -%else -BuildRequires: cmake -%endif - -%if %{?_with_tests:1}%{!?_with_tests:0} -BuildRequires: cppunit-devel -%endif - -BuildRequires: librados-devel = 2:14.2.15 -BuildRequires: libradosstriper-devel = 2:14.2.15 - -%if %{?_with_clang:1}%{!?_with_clang:0} -BuildRequires: clang -%endif - -BuildRequires: xrootd-server-devel%{?_isa} = %{epoch}:%{version}-%{release} -BuildRequires: xrootd-private-devel%{?_isa} = %{epoch}:%{version}-%{release} -BuildRequires: xrootd-libs%{?_isa} = %{epoch}:%{version}-%{release} -BuildRequires: xrootd-server-libs%{?_isa} = %{epoch}:%{version}-%{release} -BuildRequires: xrootd-client-libs%{?_isa} = %{epoch}:%{version}-%{release} - -Requires: xrootd-server-libs%{?_isa} = %{epoch}:%{version}-%{release} -Requires: xrootd-client-libs%{?_isa} = %{epoch}:%{version}-%{release} -Requires: xrootd-libs%{?_isa} = %{epoch}:%{version}-%{release} - -%description -The xrootd-ceph is an OSS layer plug-in for the XRootD server for interfacing -with the Ceph storage platform. - -#------------------------------------------------------------------------------- -# Build instructions -#------------------------------------------------------------------------------- -%prep -%setup -c -n xrootd-ceph - -%build -cd xrootd-ceph - -%if %{?_with_clang:1}%{!?_with_clang:0} -export CC=clang -export CXX=clang++ -%endif - -mkdir build -pushd build - -%if %{use_cmake3} -cmake3 \ -%else -cmake \ -%endif - -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -%if %{?_with_tests:1}%{!?_with_tests:0} - -DENABLE_TESTS=TRUE \ -%else - -DENABLE_TESTS=FALSE \ -%endif - ../ - -make -i VERBOSE=1 %{?_smp_mflags} -popd - -#------------------------------------------------------------------------------- -# Installation -#------------------------------------------------------------------------------- -%install -rm -rf $RPM_BUILD_ROOT - -#------------------------------------------------------------------------------- -# Install 4.x.y -#------------------------------------------------------------------------------- -pushd xrootd-ceph -pushd build -make install DESTDIR=$RPM_BUILD_ROOT -popd - -# ceph posix unversioned so -rm -f $RPM_BUILD_ROOT%{_libdir}/libXrdCephPosix.so - - -%clean -rm -rf $RPM_BUILD_ROOT - -#------------------------------------------------------------------------------- -# Files -#------------------------------------------------------------------------------- -%files -%defattr(-,root,root,-) -%{_libdir}/libXrdCeph-5.so -%{_libdir}/libXrdCephXattr-5.so -%{_libdir}/libXrdCephPosix.so* - -%if %{?_with_tests:1}%{!?_with_tests:0} -%files tests -%defattr(-,root,root,-) -%{_libdir}/libXrdCephTests*.so -%endif - -#------------------------------------------------------------------------------- -# Changelog -#------------------------------------------------------------------------------- -%changelog -* Wed Dec 16 2020 George Patargias -- updated version for librados-devel and libradosstriper-devel to 14.2.15 following the recent upgrade on external Echo gateways -- fixed version in xrootd-ceph shared libraries -* Mon Mar 02 2020 Michal Simon -- fixed RPM dependencies -* Thu Mar 08 2018 Michal Simon -- initial release diff --git a/src/XrdCeph/src/CMakeLists.txt b/src/XrdCeph/src/CMakeLists.txt deleted file mode 100644 index b044ee25fec..00000000000 --- a/src/XrdCeph/src/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ - -#------------------------------------------------------------------------------- -# Include the subcomponents -#------------------------------------------------------------------------------- -include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ) -if( XRDCEPH_SUBMODULE ) - add_compile_definitions( XRDCEPH_SUBMODULE ) -endif() -include( XrdCeph ) - diff --git a/src/XrdCeph/src/XrdCeph.cmake b/src/XrdCeph/src/XrdCeph.cmake deleted file mode 100644 index 5277df3fc5b..00000000000 --- a/src/XrdCeph/src/XrdCeph.cmake +++ /dev/null @@ -1,74 +0,0 @@ -#------------------------------------------------------------------------------- -# XrdCephPosix library version -#------------------------------------------------------------------------------- -set( XRD_CEPH_POSIX_VERSION 0.0.1 ) -set( XRD_CEPH_POSIX_SOVERSION 0 ) - -#------------------------------------------------------------------------------- -# The XrdCephPosix library -#------------------------------------------------------------------------------- -add_library( - XrdCephPosix - SHARED - XrdCeph/XrdCephPosix.cc XrdCeph/XrdCephPosix.hh ) - -# needed during the transition between ceph giant and ceph hammer -# for object listing API -set_property(SOURCE XrdCeph/XrdCephPosix.cc - PROPERTY COMPILE_FLAGS " -Wno-deprecated-declarations") - -target_link_libraries( - XrdCephPosix - PRIVATE - ${XROOTD_LIBRARIES} - ${RADOS_LIBS} ) - -target_include_directories( - XrdCephPosix PUBLIC ${RADOS_INCLUDE_DIR} $) - -set_target_properties( - XrdCephPosix - PROPERTIES - VERSION ${XRD_CEPH_POSIX_VERSION} - SOVERSION ${XRD_CEPH_POSIX_SOVERSION} ) - -#------------------------------------------------------------------------------- -# The XrdCeph module -#------------------------------------------------------------------------------- -set( LIB_XRD_CEPH XrdCeph-${PLUGIN_VERSION} ) - -add_library( - ${LIB_XRD_CEPH} - MODULE - XrdCeph/XrdCephOss.cc XrdCeph/XrdCephOss.hh - XrdCeph/XrdCephOssFile.cc XrdCeph/XrdCephOssFile.hh - XrdCeph/XrdCephOssDir.cc XrdCeph/XrdCephOssDir.hh ) - -target_link_libraries( - ${LIB_XRD_CEPH} - PRIVATE - ${XROOTD_LIBRARIES} - XrdCephPosix ) - -#------------------------------------------------------------------------------- -# The XrdCephXattr module -#------------------------------------------------------------------------------- -set( LIB_XRD_CEPH_XATTR XrdCephXattr-${PLUGIN_VERSION} ) - -add_library( - ${LIB_XRD_CEPH_XATTR} - MODULE - XrdCeph/XrdCephXAttr.cc XrdCeph/XrdCephXAttr.hh ) - -target_link_libraries( - ${LIB_XRD_CEPH_XATTR} - PRIVATE - ${XROOTD_LIBRARIES} - XrdCephPosix ) - -#------------------------------------------------------------------------------- -# Install -#------------------------------------------------------------------------------- -install( - TARGETS ${LIB_XRD_CEPH} ${LIB_XRD_CEPH_XATTR} XrdCephPosix - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) diff --git a/src/XrdCeph/src/XrdVersion.hh.in b/src/XrdCeph/src/XrdVersion.hh.in deleted file mode 100644 index 8ccbfa4c07b..00000000000 --- a/src/XrdCeph/src/XrdVersion.hh.in +++ /dev/null @@ -1,106 +0,0 @@ -/******************************************************************************/ -/* */ -/* X r d V e r s i o n . h h . i n */ -/* */ -/* (c) 2012 by the Board of Trustees of the Leland Stanford, Jr., University */ -/* */ -/* 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. */ -/******************************************************************************/ - -// this file is automatically updated by the genversion.sh script -// if you touch anything make sure that it still works - -#ifndef __XRD_VERSION_H__ -#define __XRD_VERSION_H__ - -#define XrdVERSION "unknown" - -// Numeric representation of the version tag -// The format for the released code is: xyyzz, where: x is the major version, -// y is the minor version and zz is the bugfix revision number -// For the non-released code the value is 1000000 -#define XrdVNUMUNK 1000000 -#define XrdVNUMBER 1000000 - -#if XrdDEBUG -#define XrdVSTRING XrdVERSION "_dbg" -#else -#define XrdVSTRING XrdVERSION -#endif - -// The following defines the shared library version number of any plug-in. -// Generally, all plug-ins have a uniform version number releative to a -// specific compilation. This version is appended to the so-name and for -// dylibs becomes part of he actual filename (MacOS format). -// -#ifndef XRDPLUGIN_SOVERSION -#define XRDPLUGIN_SOVERSION "4" -#endif - -#define XrdDEFAULTPORT 1094; - -// The following macros extract version digits from a numeric version number -#define XrdMajorVNUM(x) x/10000 -#define XrdMinorVNUM(x) x/100%100 -#define XrdPatchVNUM(x) x%100 - -// The following structure defines the standard way to record a version. You can -// determine component version numbers within an object file by simply executing -// "strings | grep '@V:'". -// -struct XrdVersionInfo {int vNum; const char vOpt; const char vPfx[3];\ - const char vStr[40];}; - -// Macro to define the suffix to use when generating the extern version symbol. -// This is used by SysPlugin. We cannot use it here as cpp does not expand the -// macro when catenating tokens togther and we want to avoid yet another macro. -// -#define XrdVERSIONINFOSFX "_" - -// The following macro defines a local copy of version information. Parameters: -// x -> The variable name of the version information structure -// y -> An unquoted 1- to 15-character component name (e.g. cmsd, seckrb5) -// vn -> The integer version number to be used -// vs -> The string version number to be used -// -#define XrdVERSIONINFODEF(x,y,vn,vs) \ - XrdVersionInfo x = \ - {vn, (sizeof(#y)-1) & 0x0f,{'@','V',':'}, #y " " vs} - -// The following macro defines an externally referencable structure that records -// the version used to compile code. It is used by the plugin loader. Parms: -// x -> The variable name of the version information structure -// y -> An unquoted 1- to 15-character component name (e.g. cmsd, seckrb5, etc). -// -#define XrdVERSIONINFO(x,y) \ - extern "C" {XrdVERSIONINFODEF(x##_,y,XrdVNUMBER,XrdVERSION);} - -// The following macro is an easy way to declare externally defined version -// information. This macro must be used at file level. -// -#define XrdVERSIONINFOREF(x) extern "C" XrdVersionInfo x##_ - -// The following macro can be used to reference externally defined version -// information. As the composition of the symbolic name may change you should -// use this macro to refer to the version information declaration. -// -#define XrdVERSIONINFOVAR(x) x##_ -#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b982b387e24..b709a9b4a5c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,6 +4,7 @@ endif() include(GoogleTest) add_subdirectory( XrdCl ) +add_subdirectory( XrdCeph ) add_subdirectory(XrdHttpTests) add_subdirectory( common ) diff --git a/src/XrdCeph/tests/CMakeLists.txt b/tests/XrdCeph/CMakeLists.txt similarity index 79% rename from src/XrdCeph/tests/CMakeLists.txt rename to tests/XrdCeph/CMakeLists.txt index 3b38c7dcd4e..19ce497b478 100644 --- a/src/XrdCeph/tests/CMakeLists.txt +++ b/tests/XrdCeph/CMakeLists.txt @@ -1,6 +1,6 @@ -find_package(GTest REQUIRED) - -include(GoogleTest) +if(NOT BUILD_CEPH) + return() +endif() add_executable(xrdceph-unit-tests XrdCeph.cc) diff --git a/src/XrdCeph/tests/XrdCeph.cc b/tests/XrdCeph/XrdCeph.cc similarity index 100% rename from src/XrdCeph/tests/XrdCeph.cc rename to tests/XrdCeph/XrdCeph.cc diff --git a/xrootd.spec b/xrootd.spec index 415e53a1956..b07567e2707 100644 --- a/xrootd.spec +++ b/xrootd.spec @@ -457,6 +457,7 @@ make -C %{_builddir}/%{name}-%{compat_version}/build %{?_smp_mflags} -DFORCE_ENABLED:BOOL=TRUE \ -DUSE_SYSTEM_ISAL:BOOL=TRUE \ -DENABLE_ASAN:BOOL=%{with asan} \ + -DENABLE_CEPH:BOOL=%{with ceph} \ -DENABLE_FUSE:BOOL=TRUE \ -DENABLE_KRB5:BOOL=TRUE \ -DENABLE_MACAROONS:BOOL=TRUE \ @@ -467,7 +468,6 @@ make -C %{_builddir}/%{name}-%{compat_version}/build %{?_smp_mflags} -DENABLE_XRDCL:BOOL=TRUE \ -DENABLE_XRDCLHTTP:BOOL=TRUE \ -DENABLE_XRDEC:BOOL=%{with xrdec} \ - -DXRDCEPH_SUBMODULE:BOOL=%{with ceph} \ -DENABLE_XRDCLHTTP:BOOL=TRUE \ -DXRDCL_ONLY:BOOL=FALSE \ -DXRDCL_LIB_ONLY:BOOL=FALSE \ From 5566e2e52aff10d533307ec7e03e8d9e4fd77ef8 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Thu, 8 Feb 2024 11:24:24 +0100 Subject: [PATCH 014/276] [Tests] Check only once if server tests can be enabled --- tests/CMakeLists.txt | 7 +++++++ tests/XRootD/CMakeLists.txt | 10 ---------- tests/cluster/CMakeLists.txt | 11 ----------- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b709a9b4a5c..a4c8a8f5c29 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,5 +16,12 @@ if( BUILD_XRDEC ) add_subdirectory( XrdEc ) # new tests with GTest endif() +execute_process(COMMAND id -u OUTPUT_VARIABLE UID + OUTPUT_STRIP_TRAILING_WHITESPACE) + +if(XRDCL_ONLY OR UID EQUAL 0) + return() +endif() + add_subdirectory( XRootD ) add_subdirectory( cluster ) diff --git a/tests/XRootD/CMakeLists.txt b/tests/XRootD/CMakeLists.txt index 7ac633ba807..d8207c4cae5 100644 --- a/tests/XRootD/CMakeLists.txt +++ b/tests/XRootD/CMakeLists.txt @@ -1,13 +1,3 @@ -if(XRDCL_ONLY) - return() -endif() - -execute_process(COMMAND id -u OUTPUT_VARIABLE UID OUTPUT_STRIP_TRAILING_WHITESPACE) - -if (UID EQUAL 0) - return() -endif() - set(XRD_TEST_PORT "11940" CACHE STRING "Port for XRootD Test Server") list(APPEND XRDENV "XRDCP=$") diff --git a/tests/cluster/CMakeLists.txt b/tests/cluster/CMakeLists.txt index 2f59c9fb55e..e9a8f431973 100644 --- a/tests/cluster/CMakeLists.txt +++ b/tests/cluster/CMakeLists.txt @@ -1,14 +1,3 @@ -if(XRDCL_ONLY) - return() -endif() - -execute_process(COMMAND id -u OUTPUT_VARIABLE UID OUTPUT_STRIP_TRAILING_WHITESPACE) - -# ensure that we're not root -if (UID EQUAL 0) - return() -endif() - list(APPEND XRDENV "XRDCP=$") list(APPEND XRDENV "XRDFS=$") list(APPEND XRDENV "CRC32C=$") From 4797d95768187005ba2f874def994f77387a721e Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Thu, 8 Feb 2024 11:31:10 +0100 Subject: [PATCH 015/276] [Tests] Remove CppUnit version of XrdEc tests --- tests/CMakeLists.txt | 18 +- tests/XrdEcTests/CMakeLists.txt | 42 -- tests/XrdEcTests/MicroTest.cc | 716 -------------------------------- 3 files changed, 10 insertions(+), 766 deletions(-) delete mode 100644 tests/XrdEcTests/CMakeLists.txt delete mode 100644 tests/XrdEcTests/MicroTest.cc diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a4c8a8f5c29..48fe5cda317 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -3,19 +3,21 @@ if(NOT BUILD_TESTS) endif() include(GoogleTest) -add_subdirectory( XrdCl ) -add_subdirectory( XrdCeph ) + +add_subdirectory(common) + +add_subdirectory(XrdCl) +add_subdirectory(XrdCeph) + +if(BUILD_XRDEC) +add_subdirectory(XrdEc) +endif() + add_subdirectory(XrdHttpTests) -add_subdirectory( common ) add_subdirectory( XrdClTests ) add_subdirectory( XrdSsiTests ) -if( BUILD_XRDEC ) - add_subdirectory( XrdEcTests ) - add_subdirectory( XrdEc ) # new tests with GTest -endif() - execute_process(COMMAND id -u OUTPUT_VARIABLE UID OUTPUT_STRIP_TRAILING_WHITESPACE) diff --git a/tests/XrdEcTests/CMakeLists.txt b/tests/XrdEcTests/CMakeLists.txt deleted file mode 100644 index e656ab4b585..00000000000 --- a/tests/XrdEcTests/CMakeLists.txt +++ /dev/null @@ -1,42 +0,0 @@ - -add_library( - XrdEcTests MODULE - MicroTest.cc -) - -target_link_libraries( - XrdEcTests - PRIVATE - XrdEc - XrdCl - XrdUtils - ${ISAL_LIBRARIES} - ${CPPUNIT_LIBRARIES}) - -target_include_directories(XrdEcTests PRIVATE ../common ${CPPUNIT_INCLUDE_DIRS} ${ISAL_INCLUDE_DIRS}) - -foreach(TEST - AlignedWriteTest - SmallWriteTest - BigWriteTest - VectorReadTest - IllegalVectorReadTest - AlignedWrite1MissingTest - AlignedWrite2MissingTest - AlignedWriteTestIsalCrcNoMt - SmallWriteTestIsalCrcNoMt - BigWriteTestIsalCrcNoMt - AlignedWrite1MissingTestIsalCrcNoMt - AlignedWrite2MissingTestIsalCrcNoMt) - add_test(NAME XrdEc::${TEST} - COMMAND $ $ - "All Tests/MicroTest/MicroTest::${TEST}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) -endforeach() - -#------------------------------------------------------------------------------- -# Install -#------------------------------------------------------------------------------- -install( - TARGETS XrdEcTests - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) diff --git a/tests/XrdEcTests/MicroTest.cc b/tests/XrdEcTests/MicroTest.cc deleted file mode 100644 index 5b375c6fe4f..00000000000 --- a/tests/XrdEcTests/MicroTest.cc +++ /dev/null @@ -1,716 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2011-2014 by European Organization for Nuclear Research (CERN) -// Author: Michal Simon -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -// -// In applying this licence, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -//------------------------------------------------------------------------------ - -#include -#include "TestEnv.hh" -#include "CppUnitXrdHelpers.hh" - -#include "XrdEc/XrdEcStrmWriter.hh" -#include "XrdEc/XrdEcReader.hh" -#include "XrdEc/XrdEcObjCfg.hh" - -#include "XrdCl/XrdClMessageUtils.hh" - -#include "XrdZip/XrdZipCDFH.hh" - -#include "XrdSys/XrdSysPlatform.hh" - -#include -#include -#include - -#include -#include -#include -#include -#include - -using namespace XrdEc; - -//------------------------------------------------------------------------------ -// Declaration -//------------------------------------------------------------------------------ -class MicroTest: public CppUnit::TestCase -{ - public: - CPPUNIT_TEST_SUITE( MicroTest ); - CPPUNIT_TEST( AlignedWriteTest ); - CPPUNIT_TEST( SmallWriteTest ); - CPPUNIT_TEST( BigWriteTest ); - CPPUNIT_TEST( VectorReadTest ); - CPPUNIT_TEST( IllegalVectorReadTest ); - CPPUNIT_TEST( AlignedWrite1MissingTest ); - CPPUNIT_TEST( AlignedWrite2MissingTest ); - CPPUNIT_TEST( AlignedWriteTestIsalCrcNoMt ); - CPPUNIT_TEST( SmallWriteTestIsalCrcNoMt ); - CPPUNIT_TEST( BigWriteTestIsalCrcNoMt ); - CPPUNIT_TEST( AlignedWrite1MissingTestIsalCrcNoMt ); - CPPUNIT_TEST( AlignedWrite2MissingTestIsalCrcNoMt ); - CPPUNIT_TEST_SUITE_END(); - - void Init( bool usecrc32c ); - - inline void AlignedWriteTestImpl( bool usecrc32c ) - { - // create the data and stripe directories - Init( usecrc32c ); - // run the test - AlignedWriteRaw(); - // verify that we wrote the data correctly - Verify(); - // clean up the data directory - CleanUp(); - } - - inline void AlignedWriteTest() - { - AlignedWriteTestImpl( true ); - } - - inline void AlignedWriteTestIsalCrcNoMt() - { - AlignedWriteTestImpl( false ); - } - - inline void VectorReadTest(){ - Init(true); - - AlignedWriteRaw(); - - Verify(); - - uint32_t seed = std::chrono::system_clock::now().time_since_epoch().count(); - - VerifyVectorRead(seed); - - CleanUp(); - } - - inline void IllegalVectorReadTest(){ - Init(true); - - AlignedWriteRaw(); - - Verify(); - - uint32_t seed = - std::chrono::system_clock::now().time_since_epoch().count(); - - IllegalVectorRead(seed); - - CleanUp(); - } - - inline void AlignedWrite1MissingTestImpl( bool usecrc32c ) - { - // initialize directories - Init( usecrc32c ); - UrlNotReachable( 2 ); - // run the test - AlignedWriteRaw(); - // verify that we wrote the data correctly - Verify(); - // clean up - UrlReachable( 2 ); - CleanUp(); - } - - inline void AlignedWrite1MissingTest() - { - AlignedWrite1MissingTestImpl( true ); - } - - inline void AlignedWrite1MissingTestIsalCrcNoMt() - { - AlignedWrite1MissingTestImpl( false ); - } - - inline void AlignedWrite2MissingTestImpl( bool usecrc32c ) - { - // initialize directories - Init( usecrc32c ); - UrlNotReachable( 2 ); - UrlNotReachable( 3 ); - // run the test - AlignedWriteRaw(); - // verify that we wrote the data correctly - Verify(); - // clean up - UrlReachable( 2 ); - UrlReachable( 3 ); - CleanUp(); - } - - inline void AlignedWrite2MissingTest() - { - AlignedWrite2MissingTestImpl( true ); - } - - inline void AlignedWrite2MissingTestIsalCrcNoMt() - { - AlignedWrite2MissingTestImpl( false ); - } - - void VarlenWriteTest( uint32_t wrtlen, bool usecrc32c ); - - inline void SmallWriteTest() - { - VarlenWriteTest( 7, true ); - } - - inline void SmallWriteTestIsalCrcNoMt() - { - VarlenWriteTest( 7, false ); - } - - void BigWriteTest() - { - VarlenWriteTest( 77, true ); - } - - void BigWriteTestIsalCrcNoMt() - { - VarlenWriteTest( 77, false ); - } - - void Verify() - { - ReadVerifyAll(); - CorruptedReadVerify(); - } - - void VerifyVectorRead(uint32_t randomSeed); - - void IllegalVectorRead(uint32_t randomSeed); - - void CleanUp(); - - inline void ReadVerifyAll() - { - AlignedReadVerify(); - PastEndReadVerify(); - SmallChunkReadVerify(); - BigChunkReadVerify(); - - for( size_t i = 0; i < 10; ++i ) - RandomReadVerify(); - } - - void ReadVerify( uint32_t rdsize, uint64_t maxrd = std::numeric_limits::max() ); - - void RandomReadVerify(); - - void Corrupted1stBlkReadVerify(); - - inline void AlignedReadVerify() - { - ReadVerify( chsize, rawdata.size() ); - } - - inline void PastEndReadVerify() - { - ReadVerify( chsize ); - } - - inline void SmallChunkReadVerify() - { - ReadVerify( 5 ); - } - - inline void BigChunkReadVerify() - { - ReadVerify( 23 ); - } - - void CorruptedReadVerify(); - - void CorruptChunk( size_t blknb, size_t strpnb ); - - void UrlNotReachable( size_t index ); - void UrlReachable( size_t index ); - - private: - - void AlignedWriteRaw(); - - void copy_rawdata( char *buffer, size_t size ) - { - const char *begin = buffer; - const char *end = begin + size; - std::copy( begin, end, std::back_inserter( rawdata ) ); - } - - std::string datadir; - std::unique_ptr objcfg; - - static const size_t nbdata = 4; - static const size_t nbparity = 2; - static const size_t chsize = 16; - static const size_t nbiters = 16; - - static const size_t lfhsize = 30; - - std::vector rawdata; -}; - -CPPUNIT_TEST_SUITE_REGISTRATION( MicroTest ); - - -void MicroTest::Init( bool usecrc32c ) -{ - objcfg.reset( new ObjCfg( "test.txt", nbdata, nbparity, chsize, usecrc32c, true ) ); - rawdata.clear(); - - char tmpdir[MAXPATHLEN]; - CPPUNIT_ASSERT( getcwd(tmpdir, MAXPATHLEN - 21) ); - strcat(tmpdir, "/xrootd-xrdec-XXXXXX"); - // create the data directory - CPPUNIT_ASSERT( mkdtemp(tmpdir) ); - datadir = tmpdir; - // create a directory for each stripe - size_t nbstrps = objcfg->nbdata + 2 * objcfg->nbparity; - for( size_t i = 0; i < nbstrps; ++i ) - { - std::stringstream ss; - ss << std::setfill('0') << std::setw( 2 ) << i; - std::string strp = datadir + '/' + ss.str() + '/'; - objcfg->plgr.emplace_back( strp ); - CPPUNIT_ASSERT( mkdir( strp.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) == 0 ); - } -} - -void MicroTest::CorruptChunk( size_t blknb, size_t strpnb ) -{ - Reader reader( *objcfg ); - // open the data object - XrdCl::SyncResponseHandler handler1; - reader.Open( &handler1 ); - handler1.WaitForResponse(); - XrdCl::XRootDStatus *status = handler1.GetStatus(); - CPPUNIT_ASSERT_XRDST( *status ); - delete status; - - // get the CD buffer - std::string fn = objcfg->GetFileName( blknb, strpnb ); - std::string url = reader.urlmap[fn]; - buffer_t cdbuff = reader.dataarchs[url]->GetCD(); - - // close the data object - XrdCl::SyncResponseHandler handler2; - reader.Close( &handler2 ); - handler2.WaitForResponse(); - status = handler2.GetStatus(); - CPPUNIT_ASSERT_XRDST( *status ); - delete status; - - // parse the CD buffer - const char *buff = cdbuff.data(); - size_t size = cdbuff.size(); - XrdZip::cdvec_t cdvec; - XrdZip::cdmap_t cdmap; - std::tie(cdvec, cdmap ) = XrdZip::CDFH::Parse( buff, size ); - - // now corrupt the chunk (put wrong checksum) - XrdZip::CDFH &cdfh = *cdvec[cdmap[fn]]; - uint64_t offset = cdfh.offset + lfhsize + fn.size(); // offset of the data - XrdCl::File f; - XrdCl::XRootDStatus status2 = f.Open( url, XrdCl::OpenFlags::Write ); - CPPUNIT_ASSERT_XRDST( status2 ); - std::string str = "XXXXXXXX"; - status2 = f.Write( offset, str.size(), str.c_str() ); - CPPUNIT_ASSERT_XRDST( status2 ); - status2 = f.Close(); - CPPUNIT_ASSERT_XRDST( status2 ); -} - -void MicroTest::UrlNotReachable( size_t index ) -{ - XrdCl::URL url( objcfg->plgr[index] ); - CPPUNIT_ASSERT( chmod( url.GetPath().c_str(), 0 ) == 0 ); -} - -void MicroTest::UrlReachable( size_t index ) -{ - XrdCl::URL url( objcfg->plgr[index] ); - mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | - S_IXUSR | S_IXGRP | S_IXOTH; - CPPUNIT_ASSERT( chmod( url.GetPath().c_str(), mode ) == 0 ); -} - -void MicroTest::CorruptedReadVerify() -{ - UrlNotReachable( 0 ); - ReadVerifyAll(); - UrlNotReachable( 1 ); - ReadVerifyAll(); - UrlReachable( 0 ); - UrlReachable( 1 ); - - CorruptChunk( 0, 1 ); - ReadVerifyAll(); - - CorruptChunk( 0, 2 ); - ReadVerifyAll(); - -} - -void MicroTest::VerifyVectorRead(uint32_t seed){ - Reader reader( *objcfg ); - // open the data object - XrdCl::SyncResponseHandler handler1; - reader.Open( &handler1 ); - handler1.WaitForResponse(); - XrdCl::XRootDStatus *status = handler1.GetStatus(); - CPPUNIT_ASSERT_XRDST( *status ); - delete status; - - std::default_random_engine random_engine(seed); - - std::vector> buffers(5); - std::vector expected; - XrdCl::ChunkList chunks; - for(int i = 0; i < 5; i++){ - std::uniform_int_distribution sizeGen(0, rawdata.size()/4); - uint32_t size = sizeGen(random_engine); - std::uniform_int_distribution offsetGen(0, rawdata.size() - size); - uint32_t offset = offsetGen(random_engine); - - buffers[i].resize(size); - chunks.push_back(XrdCl::ChunkInfo(offset, size, buffers[i].data())); - - std::string resultExp( rawdata.data() + offset, size ); - expected.push_back(resultExp); - } - - XrdCl::SyncResponseHandler h; - reader.VectorRead(chunks, nullptr, &h, 0); - h.WaitForResponse(); - status = h.GetStatus(); - CPPUNIT_ASSERT_XRDST( *status ); - delete status; - for(int i = 0; i < 5; i++){ - std::string result(buffers[i].data(), expected[i].size()); - CPPUNIT_ASSERT( result == expected[i] ); - } - - XrdCl::SyncResponseHandler handler2; - reader.Close( &handler2 ); - handler2.WaitForResponse(); - status = handler2.GetStatus(); - CPPUNIT_ASSERT_XRDST( *status ); - delete status; -} - -void MicroTest::IllegalVectorRead(uint32_t seed){ - Reader reader(*objcfg); - // open the data object - XrdCl::SyncResponseHandler handler1; - reader.Open(&handler1); - handler1.WaitForResponse(); - XrdCl::XRootDStatus *status = handler1.GetStatus(); - CPPUNIT_ASSERT_XRDST(*status); - delete status; - - std::default_random_engine random_engine(seed); - - std::vector> buffers(5); - XrdCl::ChunkList chunks; - for (int i = 0; i < 5; i++) - { - std::uniform_int_distribution sizeGen(1, rawdata.size() / 4); - uint32_t size = sizeGen(random_engine); - std::uniform_int_distribution offsetGen(0, - rawdata.size() - size); - uint32_t offset = offsetGen(random_engine); - if (i == 0) - offset = rawdata.size() - size / 2; - - buffers[i].resize(size); - - chunks.push_back(XrdCl::ChunkInfo(offset, size, buffers[i].data())); - - } - - XrdCl::SyncResponseHandler h; - reader.VectorRead(chunks, nullptr, &h, 0); - h.WaitForResponse(); - status = h.GetStatus(); - // the response should be negative since one of the reads was over the file end - if (status->IsOK()) - { - CPPUNIT_ASSERT(false); - } - delete status; - - buffers.clear(); - buffers.resize(1025); - chunks.clear(); - for (int i = 0; i < 1025; i++) - { - std::uniform_int_distribution sizeGen(1, rawdata.size() / 4); - uint32_t size = sizeGen(random_engine); - std::uniform_int_distribution offsetGen(0, - rawdata.size() - size); - uint32_t offset = offsetGen(random_engine); - - buffers[i].resize(size); - - chunks.push_back(XrdCl::ChunkInfo(offset, size, buffers[i].data())); - - } - - XrdCl::SyncResponseHandler h2; - reader.VectorRead(chunks, nullptr, &h2, 0); - h2.WaitForResponse(); - status = h2.GetStatus(); - // the response should be negative since we requested too many reads - if (status->IsOK()) - { - CPPUNIT_ASSERT(false); - } - delete status; - - XrdCl::SyncResponseHandler handler2; - reader.Close(&handler2); - handler2.WaitForResponse(); - status = handler2.GetStatus(); - CPPUNIT_ASSERT_XRDST(*status); - delete status; -} - -void MicroTest::ReadVerify( uint32_t rdsize, uint64_t maxrd ) -{ - Reader reader( *objcfg ); - // open the data object - XrdCl::SyncResponseHandler handler1; - reader.Open( &handler1 ); - handler1.WaitForResponse(); - XrdCl::XRootDStatus *status = handler1.GetStatus(); - CPPUNIT_ASSERT_XRDST( *status ); - delete status; - - uint64_t rdoff = 0; - char *rdbuff = new char[rdsize]; - uint32_t bytesrd = 0; - uint64_t total_bytesrd = 0; - do - { - XrdCl::SyncResponseHandler h; - reader.Read( rdoff, rdsize, rdbuff, &h, 0 ); - h.WaitForResponse(); - status = h.GetStatus(); - CPPUNIT_ASSERT_XRDST( *status ); - // get the actual result - auto rsp = h.GetResponse(); - XrdCl::ChunkInfo *ch = nullptr; - rsp->Get( ch ); - bytesrd = ch->length; - std::string result( reinterpret_cast( ch->buffer ), bytesrd ); - // get the expected result - size_t rawoff = rdoff; - size_t rawsz = rdsize; - if( rawoff + rawsz > rawdata.size() ) rawsz = rawdata.size() - rawoff; - std::string expected( rawdata.data() + rawoff, rawsz ); - // make sure the expected and actual results are the same - CPPUNIT_ASSERT( result == expected ); - delete status; - delete rsp; - rdoff += bytesrd; - total_bytesrd += bytesrd; - } - while( bytesrd == rdsize && total_bytesrd < maxrd ); - delete[] rdbuff; - - // close the data object - XrdCl::SyncResponseHandler handler2; - reader.Close( &handler2 ); - handler2.WaitForResponse(); - status = handler2.GetStatus(); - CPPUNIT_ASSERT_XRDST( *status ); - delete status; -} - -void MicroTest::RandomReadVerify() -{ - size_t filesize = rawdata.size(); - static std::default_random_engine random_engine( std::chrono::system_clock::now().time_since_epoch().count() ); - std::uniform_int_distribution offdistr( 0, filesize ); - uint64_t rdoff = offdistr( random_engine ); - std::uniform_int_distribution lendistr( rdoff, filesize + 32 ); - uint32_t rdlen = lendistr( random_engine ); - - Reader reader( *objcfg ); - // open the data object - XrdCl::SyncResponseHandler handler1; - reader.Open( &handler1 ); - handler1.WaitForResponse(); - XrdCl::XRootDStatus *status = handler1.GetStatus(); - CPPUNIT_ASSERT_XRDST( *status ); - delete status; - - // read the data - char *rdbuff = new char[rdlen]; - XrdCl::SyncResponseHandler h; - reader.Read( rdoff, rdlen, rdbuff, &h, 0 ); - h.WaitForResponse(); - status = h.GetStatus(); - CPPUNIT_ASSERT_XRDST( *status ); - // get the actual result - auto rsp = h.GetResponse(); - XrdCl::ChunkInfo *ch = nullptr; - rsp->Get( ch ); - uint32_t bytesrd = ch->length; - std::string result( reinterpret_cast( ch->buffer ), bytesrd ); - // get the expected result - size_t rawoff = rdoff; - size_t rawlen = rdlen; - if( rawoff > rawdata.size() ) rawlen = 0; - else if( rawoff + rawlen > rawdata.size() ) rawlen = rawdata.size() - rawoff; - std::string expected( rawdata.data() + rawoff, rawlen ); - // make sure the expected and actual results are the same - CPPUNIT_ASSERT( result == expected ); - delete status; - delete rsp; - delete[] rdbuff; - - // close the data object - XrdCl::SyncResponseHandler handler2; - reader.Close( &handler2 ); - handler2.WaitForResponse(); - status = handler2.GetStatus(); - CPPUNIT_ASSERT_XRDST( *status ); - delete status; -} - -void MicroTest::Corrupted1stBlkReadVerify() -{ - uint64_t rdoff = 0; - uint32_t rdlen = objcfg->datasize; - - Reader reader( *objcfg ); - // open the data object - XrdCl::SyncResponseHandler handler1; - reader.Open( &handler1 ); - handler1.WaitForResponse(); - XrdCl::XRootDStatus *status = handler1.GetStatus(); - CPPUNIT_ASSERT_XRDST( *status ); - delete status; - - // read the data - char *rdbuff = new char[rdlen]; - XrdCl::SyncResponseHandler h; - reader.Read( rdoff, rdlen, rdbuff, &h, 0 ); - h.WaitForResponse(); - status = h.GetStatus(); - CPPUNIT_ASSERT( status->status == XrdCl::stError && - status->code == XrdCl::errDataError ); - delete status; - delete[] rdbuff; - - // close the data object - XrdCl::SyncResponseHandler handler2; - reader.Close( &handler2 ); - handler2.WaitForResponse(); - status = handler2.GetStatus(); - CPPUNIT_ASSERT_XRDST( *status ); - delete status; -} - -int unlink_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) -{ - int rc = remove( fpath ); - CPPUNIT_ASSERT( rc == 0 ); - return rc; -} - -void MicroTest::CleanUp() -{ - // delete the data directory - nftw( datadir.c_str(), unlink_cb, 64, FTW_DEPTH | FTW_PHYS ); -} - -void MicroTest::AlignedWriteRaw() -{ - char buffer[objcfg->chunksize]; - StrmWriter writer( *objcfg ); - // open the data object - XrdCl::SyncResponseHandler handler1; - writer.Open( &handler1 ); - handler1.WaitForResponse(); - XrdCl::XRootDStatus *status = handler1.GetStatus(); - CPPUNIT_ASSERT_XRDST( *status ); - delete status; - // write to the data object - for( size_t i = 0; i < nbiters; ++i ) - { - memset( buffer, 'A' + i, objcfg->chunksize ); - writer.Write( objcfg->chunksize, buffer, nullptr ); - copy_rawdata( buffer, sizeof( buffer ) ); - } - XrdCl::SyncResponseHandler handler2; - writer.Close( &handler2 ); - handler2.WaitForResponse(); - status = handler2.GetStatus(); - CPPUNIT_ASSERT_XRDST( *status ); - delete status; -} - -void MicroTest::VarlenWriteTest( uint32_t wrtlen, bool usecrc32c ) -{ - // create the data and stripe directories - Init( usecrc32c ); - // open the data object - StrmWriter writer( *objcfg ); - XrdCl::SyncResponseHandler handler1; - writer.Open( &handler1 ); - handler1.WaitForResponse(); - XrdCl::XRootDStatus *status = handler1.GetStatus(); - CPPUNIT_ASSERT_XRDST( *status ); - delete status; - // write the data - char wrtbuff[wrtlen]; - size_t bytesleft = nbiters * objcfg->chunksize; - size_t i = 0; - while( bytesleft > 0 ) - { - if( wrtlen > bytesleft ) wrtlen = bytesleft; - memset( wrtbuff, 'A' + i, wrtlen ); - writer.Write( wrtlen, wrtbuff, nullptr ); - copy_rawdata( wrtbuff, wrtlen ); - bytesleft -= wrtlen; - ++i; - } - XrdCl::SyncResponseHandler handler2; - writer.Close( &handler2 ); - handler2.WaitForResponse(); - status = handler2.GetStatus(); - CPPUNIT_ASSERT_XRDST( *status ); - delete status; - - // verify that we wrote the data correctly - Verify(); - // clean up the data directory - CleanUp(); -} - From 2ee9c9dc477b2a7bbe077e1f39ed38f931967e71 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Thu, 8 Feb 2024 11:43:19 +0100 Subject: [PATCH 016/276] [Tests] Remove CppUnit version of XrdCl tests --- tests/CMakeLists.txt | 1 - tests/XrdClTests/CMakeLists.txt | 71 -- tests/XrdClTests/FileCopyTest.cc | 651 -------------- tests/XrdClTests/FileSystemTest.cc | 689 -------------- tests/XrdClTests/FileTest.cc | 811 ----------------- tests/XrdClTests/IdentityPlugIn.cc | 488 ---------- tests/XrdClTests/IdentityPlugIn.hh | 55 -- tests/XrdClTests/LocalFileHandlerTest.cc | 550 ------------ tests/XrdClTests/MonitorTestLib.cc | 213 ----- tests/XrdClTests/OperationsWorkflowTest.cc | 991 --------------------- tests/XrdClTests/PollerTest.cc | 280 ------ tests/XrdClTests/PostMasterTest.cc | 581 ------------ tests/XrdClTests/SocketTest.cc | 307 ------- tests/XrdClTests/ThreadingTest.cc | 348 -------- tests/XrdClTests/UtilsTest.cc | 264 ------ tests/XrdClTests/XRootDProtocolHelper.cc | 118 --- tests/XrdClTests/XRootDProtocolHelper.hh | 45 - tests/XrdClTests/cppunit.supp | 17 - tests/XrdClTests/printenv.sh | 24 - tests/XrdClTests/tls/CMakeLists.txt | 32 - tests/XrdClTests/tls/README.md | 19 - tests/XrdClTests/tls/xrdcl-tls.cc | 70 -- tests/XrdClTests/tls/xrdsrv-tls.cc | 698 --------------- tests/XrdClTests/wrt/xrdsrv-dio.cc | 602 ------------- tests/XrdClTests/wrt/xrdsrv-wrt.cc | 563 ------------ 25 files changed, 8488 deletions(-) delete mode 100644 tests/XrdClTests/CMakeLists.txt delete mode 100644 tests/XrdClTests/FileCopyTest.cc delete mode 100644 tests/XrdClTests/FileSystemTest.cc delete mode 100644 tests/XrdClTests/FileTest.cc delete mode 100644 tests/XrdClTests/IdentityPlugIn.cc delete mode 100644 tests/XrdClTests/IdentityPlugIn.hh delete mode 100644 tests/XrdClTests/LocalFileHandlerTest.cc delete mode 100644 tests/XrdClTests/MonitorTestLib.cc delete mode 100644 tests/XrdClTests/OperationsWorkflowTest.cc delete mode 100644 tests/XrdClTests/PollerTest.cc delete mode 100644 tests/XrdClTests/PostMasterTest.cc delete mode 100644 tests/XrdClTests/SocketTest.cc delete mode 100644 tests/XrdClTests/ThreadingTest.cc delete mode 100644 tests/XrdClTests/UtilsTest.cc delete mode 100644 tests/XrdClTests/XRootDProtocolHelper.cc delete mode 100644 tests/XrdClTests/XRootDProtocolHelper.hh delete mode 100644 tests/XrdClTests/cppunit.supp delete mode 100755 tests/XrdClTests/printenv.sh delete mode 100644 tests/XrdClTests/tls/CMakeLists.txt delete mode 100644 tests/XrdClTests/tls/README.md delete mode 100644 tests/XrdClTests/tls/xrdcl-tls.cc delete mode 100644 tests/XrdClTests/tls/xrdsrv-tls.cc delete mode 100644 tests/XrdClTests/wrt/xrdsrv-dio.cc delete mode 100644 tests/XrdClTests/wrt/xrdsrv-wrt.cc diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 48fe5cda317..84f3a653f12 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -15,7 +15,6 @@ endif() add_subdirectory(XrdHttpTests) -add_subdirectory( XrdClTests ) add_subdirectory( XrdSsiTests ) execute_process(COMMAND id -u OUTPUT_VARIABLE UID diff --git a/tests/XrdClTests/CMakeLists.txt b/tests/XrdClTests/CMakeLists.txt deleted file mode 100644 index 238e2509899..00000000000 --- a/tests/XrdClTests/CMakeLists.txt +++ /dev/null @@ -1,71 +0,0 @@ - -add_subdirectory( tls ) - -set( LIB_XRD_CL_TEST_MONITOR XrdClTestMonitor-${PLUGIN_VERSION} ) - - -if( XrdClPipelines ) - set( OperationsWorkflowTest OperationsWorkflowTest.cc ) -endif() - -add_library( - XrdClTests MODULE - UtilsTest.cc - SocketTest.cc - PollerTest.cc - PostMasterTest.cc - FileSystemTest.cc - FileTest.cc - FileCopyTest.cc - ThreadingTest.cc - IdentityPlugIn.cc - LocalFileHandlerTest.cc - - ${OperationsWorkflowTest} -) - -target_link_libraries( - XrdClTests - XrdClTestsHelper - ${CMAKE_THREAD_LIBS_INIT} - ${CPPUNIT_LIBRARIES} - ZLIB::ZLIB - XrdCl ) - -target_include_directories( XrdClTests PRIVATE ../common ${CPPUNIT_INCLUDE_DIRS} ) - -add_library( - ${LIB_XRD_CL_TEST_MONITOR} MODULE - MonitorTestLib.cc -) - -target_link_libraries( - ${LIB_XRD_CL_TEST_MONITOR} - XrdClTestsHelper - XrdCl ) - -target_include_directories( ${LIB_XRD_CL_TEST_MONITOR} PRIVATE ../common ) - -foreach(TEST_SUITE - # File - # FileCopy - # FileSystem - # LocalFileHandler - Poller - # PostMaster - Socket - # Threading - Utils - # Workflow -) - add_test(NAME XrdCl::${TEST_SUITE} - COMMAND $ $ "All Tests/${TEST_SUITE}Test") - set_tests_properties(XrdCl::${TEST_SUITE} PROPERTIES RUN_SERIAL TRUE) -endforeach() - -#------------------------------------------------------------------------------- -# Install -#------------------------------------------------------------------------------- -install( - TARGETS XrdClTests ${LIB_XRD_CL_TEST_MONITOR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) diff --git a/tests/XrdClTests/FileCopyTest.cc b/tests/XrdClTests/FileCopyTest.cc deleted file mode 100644 index 90e6845e795..00000000000 --- a/tests/XrdClTests/FileCopyTest.cc +++ /dev/null @@ -1,651 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2011-2012 by European Organization for Nuclear Research (CERN) -// Author: Lukasz Janyst -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -//------------------------------------------------------------------------------ - -#include -#include "TestEnv.hh" -#include "CppUnitXrdHelpers.hh" -#include "XrdCl/XrdClFile.hh" -#include "XrdCl/XrdClDefaultEnv.hh" -#include "XrdCl/XrdClMessage.hh" -#include "XrdCl/XrdClSIDManager.hh" -#include "XrdCl/XrdClPostMaster.hh" -#include "XrdCl/XrdClXRootDTransport.hh" -#include "XrdCl/XrdClMessageUtils.hh" -#include "XrdCl/XrdClXRootDMsgHandler.hh" -#include "XrdCl/XrdClUtils.hh" -#include "XrdCl/XrdClCheckSumManager.hh" -#include "XrdCl/XrdClCopyProcess.hh" - -#include "XrdCks/XrdCks.hh" -#include "XrdCks/XrdCksCalc.hh" -#include "XrdCks/XrdCksData.hh" - -#include -#include -#include - -using namespace XrdClTests; - -//------------------------------------------------------------------------------ -// Declaration -//------------------------------------------------------------------------------ -class FileCopyTest: public CppUnit::TestCase -{ - public: - CPPUNIT_TEST_SUITE( FileCopyTest ); - CPPUNIT_TEST( DownloadTest ); - CPPUNIT_TEST( UploadTest ); - CPPUNIT_TEST( MultiStreamDownloadTest ); - CPPUNIT_TEST( MultiStreamUploadTest ); - CPPUNIT_TEST( ThirdPartyCopyTest ); - CPPUNIT_TEST( NormalCopyTest ); - CPPUNIT_TEST_SUITE_END(); - void DownloadTestFunc(); - void UploadTestFunc(); - void DownloadTest(); - void UploadTest(); - void MultiStreamDownloadTest(); - void MultiStreamUploadTest(); - void CopyTestFunc( bool thirdParty = true ); - void ThirdPartyCopyTest(); - void NormalCopyTest(); -}; - -CPPUNIT_TEST_SUITE_REGISTRATION( FileCopyTest ); - -//------------------------------------------------------------------------------ -// Download test -//------------------------------------------------------------------------------ -void FileCopyTest::DownloadTestFunc() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string remoteFile; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "RemoteFile", remoteFile ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - std::string fileUrl = address + "/" + remoteFile; - - const uint32_t MB = 1024*1024; - char *buffer = new char[4*MB]; - StatInfo *stat = 0; - File f; - - //---------------------------------------------------------------------------- - // Open and stat the file - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( f.Open( fileUrl, OpenFlags::Read ) ); - - CPPUNIT_ASSERT_XRDST( f.Stat( false, stat ) ); - CPPUNIT_ASSERT( stat ); - CPPUNIT_ASSERT( stat->TestFlags( StatInfo::IsReadable ) ); - - //---------------------------------------------------------------------------- - // Fetch the data - //---------------------------------------------------------------------------- - uint64_t totalRead = 0; - uint32_t bytesRead = 0; - - CheckSumManager *man = DefaultEnv::GetCheckSumManager(); - XrdCksCalc *crc32Sum = man->GetCalculator("zcrc32"); - CPPUNIT_ASSERT( crc32Sum ); - - while( 1 ) - { - CPPUNIT_ASSERT_XRDST( f.Read( totalRead, 4*MB, buffer, bytesRead ) ); - if( bytesRead == 0 ) - break; - totalRead += bytesRead; - crc32Sum->Update( buffer, bytesRead ); - } - - //---------------------------------------------------------------------------- - // Compare the checksums - //---------------------------------------------------------------------------- - char crcBuff[9]; - XrdCksData crc; crc.Set( (const void *)crc32Sum->Final(), 4 ); crc.Get( crcBuff, 9 ); - std::string transferSum = "zcrc32:"; transferSum += crcBuff; - - std::string remoteSum; - std::string lastUrl; - CPPUNIT_ASSERT( f.GetProperty( "LastURL", lastUrl ) ); - CPPUNIT_ASSERT_XRDST( Utils::GetRemoteCheckSum( remoteSum, "zcrc32", - URL( lastUrl ) ) ); - CPPUNIT_ASSERT( remoteSum == transferSum ); - - delete stat; - delete crc32Sum; - delete[] buffer; -} - -//------------------------------------------------------------------------------ -// Upload test -//------------------------------------------------------------------------------ -void FileCopyTest::UploadTestFunc() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - std::string localFile; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "DataPath", dataPath ) ); - CPPUNIT_ASSERT( testEnv->GetString( "LocalFile", localFile ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - std::string fileUrl = address + "/" + dataPath + "/testUpload.dat"; - std::string remoteFile = dataPath + "/testUpload.dat"; - - const uint32_t MB = 1024*1024; - char *buffer = new char[4*MB]; - File f; - - //---------------------------------------------------------------------------- - // Open - //---------------------------------------------------------------------------- - int fd = -1; - CPPUNIT_ASSERT_ERRNO( (fd=open( localFile.c_str(), O_RDONLY )) > 0 ) - CPPUNIT_ASSERT_XRDST( f.Open( fileUrl, - OpenFlags::Delete|OpenFlags::Update ) ); - - //---------------------------------------------------------------------------- - // Read the data - //---------------------------------------------------------------------------- - uint64_t offset = 0; - ssize_t bytesRead; - - CheckSumManager *man = DefaultEnv::GetCheckSumManager(); - XrdCksCalc *crc32Sum = man->GetCalculator("zcrc32"); - CPPUNIT_ASSERT( crc32Sum ); - - while( (bytesRead = read( fd, buffer, 4*MB )) > 0 ) - { - crc32Sum->Update( buffer, bytesRead ); - CPPUNIT_ASSERT_XRDST( f.Write( offset, bytesRead, buffer ) ); - offset += bytesRead; - } - - CPPUNIT_ASSERT( bytesRead >= 0 ); - close( fd ); - CPPUNIT_ASSERT_XRDST( f.Close() ); - delete [] buffer; - - //---------------------------------------------------------------------------- - // Find out which server has the file - //---------------------------------------------------------------------------- - FileSystem fs( url ); - LocationInfo *locations = 0; - CPPUNIT_ASSERT_XRDST( fs.DeepLocate( remoteFile, OpenFlags::Refresh, locations ) ); - CPPUNIT_ASSERT( locations ); - CPPUNIT_ASSERT( locations->GetSize() != 0 ); - FileSystem fs1( locations->Begin()->GetAddress() ); - delete locations; - - //---------------------------------------------------------------------------- - // Verify the size - //---------------------------------------------------------------------------- - StatInfo *stat = 0; - CPPUNIT_ASSERT_XRDST( fs1.Stat( remoteFile, stat ) ); - CPPUNIT_ASSERT( stat ); - CPPUNIT_ASSERT( stat->GetSize() == offset ); - - //---------------------------------------------------------------------------- - // Compare the checksums - //---------------------------------------------------------------------------- - char crcBuff[9]; - XrdCksData crc; crc.Set( (const void *)crc32Sum->Final(), 4 ); crc.Get( crcBuff, 9 ); - std::string transferSum = "zcrc32:"; transferSum += crcBuff; - - std::string remoteSum, lastUrl; - f.GetProperty( "LastURL", lastUrl ); - CPPUNIT_ASSERT_XRDST( Utils::GetRemoteCheckSum( remoteSum, "zcrc32", - lastUrl ) ); - CPPUNIT_ASSERT( remoteSum == transferSum ); - - //---------------------------------------------------------------------------- - // Delete the file - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( fs.Rm( dataPath + "/testUpload.dat" ) ); - - delete stat; - delete crc32Sum; -} - -//------------------------------------------------------------------------------ -// Upload test -//------------------------------------------------------------------------------ -void FileCopyTest::UploadTest() -{ - UploadTestFunc(); -} - -void FileCopyTest::MultiStreamUploadTest() -{ - XrdCl::Env *env = XrdCl::DefaultEnv::GetEnv(); - env->PutInt( "SubStreamsPerChannel", 4 ); - UploadTestFunc(); -} - -//------------------------------------------------------------------------------ -// Download test -//------------------------------------------------------------------------------ -void FileCopyTest::DownloadTest() -{ - DownloadTestFunc(); -} - -void FileCopyTest::MultiStreamDownloadTest() -{ - XrdCl::Env *env = XrdCl::DefaultEnv::GetEnv(); - env->PutInt( "SubStreamsPerChannel", 4 ); - DownloadTestFunc(); -} - -namespace -{ - //---------------------------------------------------------------------------- - // Abort handler - //---------------------------------------------------------------------------- - class CancelProgressHandler: public XrdCl::CopyProgressHandler - { - public: - //------------------------------------------------------------------------ - // Constructor/destructor - //------------------------------------------------------------------------ - CancelProgressHandler(): pCancel( false ) {} - virtual ~CancelProgressHandler() {}; - - //------------------------------------------------------------------------ - // Job progress - //------------------------------------------------------------------------ - virtual void JobProgress( uint16_t jobNum, - uint64_t bytesProcessed, - uint64_t bytesTotal ) - { - if( bytesProcessed > 128*1024*1024 ) - pCancel = true; - } - - //------------------------------------------------------------------------ - // Determine whether the job should be canceled - //------------------------------------------------------------------------ - virtual bool ShouldCancel( uint16_t jobNum ) { return pCancel; } - - private: - bool pCancel; - }; -} - -//------------------------------------------------------------------------------ -// Third party copy test -//------------------------------------------------------------------------------ -void FileCopyTest::CopyTestFunc( bool thirdParty ) -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string metamanager; - std::string manager1; - std::string manager2; - std::string sourceFile; - std::string dataPath; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", metamanager ) ); - CPPUNIT_ASSERT( testEnv->GetString( "Manager1URL", manager1 ) ); - CPPUNIT_ASSERT( testEnv->GetString( "Manager2URL", manager2 ) ); - CPPUNIT_ASSERT( testEnv->GetString( "RemoteFile", sourceFile ) ); - CPPUNIT_ASSERT( testEnv->GetString( "DataPath", dataPath ) ); - - std::string sourceURL = manager1 + "/" + sourceFile; - std::string targetPath = dataPath + "/tpcFile"; - std::string targetURL = manager2 + "/" + targetPath; - std::string metalinkURL = metamanager + "/" + dataPath + "/metalink/mlTpcTest.meta4"; - std::string metalinkURL2 = metamanager + "/" + dataPath + "/metalink/mlZipTest.meta4"; - std::string zipURL = metamanager + "/" + dataPath + "/data.zip"; - std::string zipURL2 = metamanager + "/" + dataPath + "/large.zip"; - std::string fileInZip = "paper.txt"; - std::string fileInZip2 = "bible.txt"; - std::string xcpSourceURL = metamanager + "/" + dataPath + "/1db882c8-8cd6-4df1-941f-ce669bad3458.dat"; - std::string localFile = "/data/localfile.dat"; - - CopyProcess process1, process2, process3, process4, process5, process6, process7, process8, process9, - process10, process11, process12, process13, process14, process15, process16, process17; - PropertyList properties, results; - FileSystem fs( manager2 ); - - //---------------------------------------------------------------------------- - // Copy from a ZIP archive - //---------------------------------------------------------------------------- - if( !thirdParty ) - { - results.Clear(); - properties.Set( "source", zipURL ); - properties.Set( "target", targetURL ); - properties.Set( "zipArchive", true ); - properties.Set( "zipSource", fileInZip ); - CPPUNIT_ASSERT_XRDST( process6.AddJob( properties, &results ) ); - CPPUNIT_ASSERT_XRDST( process6.Prepare() ); - CPPUNIT_ASSERT_XRDST( process6.Run(0) ); - CPPUNIT_ASSERT_XRDST( fs.Rm( targetPath ) ); - properties.Clear(); - - //-------------------------------------------------------------------------- - // Copy from a ZIP archive (compressed) and validate the zcrc32 checksum - //-------------------------------------------------------------------------- - results.Clear(); - properties.Set( "source", zipURL2 ); - properties.Set( "target", targetURL ); - properties.Set( "checkSumMode", "end2end" ); - properties.Set( "checkSumType", "zcrc32" ); - properties.Set( "zipArchive", true ); - properties.Set( "zipSource", fileInZip2 ); - CPPUNIT_ASSERT_XRDST( process10.AddJob( properties, &results ) ); - CPPUNIT_ASSERT_XRDST( process10.Prepare() ); - CPPUNIT_ASSERT_XRDST( process10.Run(0) ); - CPPUNIT_ASSERT_XRDST( fs.Rm( targetPath ) ); - properties.Clear(); - - //-------------------------------------------------------------------------- - // Copy with `--rm-bad-cksum` - //-------------------------------------------------------------------------- - results.Clear(); - properties.Set( "source", sourceURL ); - properties.Set( "target", targetURL ); - properties.Set( "checkSumMode", "end2end" ); - properties.Set( "checkSumType", "auto" ); - properties.Set( "checkSumPreset", "bad-value" ); //< provide wrong checksum value, so the check fails and the file gets removed - properties.Set( "rmOnBadCksum", true ); - CPPUNIT_ASSERT_XRDST( process12.AddJob( properties, &results ) ); - CPPUNIT_ASSERT_XRDST( process12.Prepare() ); - CPPUNIT_ASSERT_XRDST_NOTOK( process12.Run(0), XrdCl::errCheckSumError ); - XrdCl::StatInfo *info = 0; - XrdCl::XRootDStatus status = fs.Stat( targetPath, info ); - CPPUNIT_ASSERT_XRDST( status.status == XrdCl::stError && status.code == XrdCl::errNotFound ); - properties.Clear(); - - //-------------------------------------------------------------------------- - // Copy with `--zip-mtln-cksum` - //-------------------------------------------------------------------------- - results.Clear(); - properties.Set( "source", metalinkURL2 ); - properties.Set( "target", targetURL ); - properties.Set( "checkSumMode", "end2end" ); - properties.Set( "checkSumType", "zcrc32" ); - XrdCl::Env *env = XrdCl::DefaultEnv::GetEnv(); - env->PutInt( "ZipMtlnCksum", 1 ); - CPPUNIT_ASSERT_XRDST( process13.AddJob( properties, &results ) ); - CPPUNIT_ASSERT_XRDST( process13.Prepare() ); - CPPUNIT_ASSERT_XRDST_NOTOK( process13.Run(0), XrdCl::errCheckSumError ); - env->PutInt( "ZipMtlnCksum", 0 ); - CPPUNIT_ASSERT_XRDST( fs.Rm( targetPath ) ); - - //-------------------------------------------------------------------------- - // Copy with - // `--xrate` - // `--xrate-threshold` - //-------------------------------------------------------------------------- - results.Clear(); - properties.Clear(); - properties.Set( "source", sourceURL ); - properties.Set( "target", targetURL ); - properties.Set( "xrate", 1024 * 1024 * 32 ); //< limit the transfer rate to 32MB/s - properties.Set( "xrateThreshold", 1024 * 1024 * 30 ); //< fail the job if the transfer rate drops under 30MB/s - CPPUNIT_ASSERT_XRDST( process14.AddJob( properties, &results ) ); - CPPUNIT_ASSERT_XRDST( process14.Prepare() ); - CPPUNIT_ASSERT_XRDST( process14.Run(0) ); - CPPUNIT_ASSERT_XRDST( fs.Rm( targetPath ) ); - - //-------------------------------------------------------------------------- - // Now test the cp-timeout - //-------------------------------------------------------------------------- - results.Clear(); - properties.Clear(); - properties.Set( "source", sourceURL ); - properties.Set( "target", targetURL ); - properties.Set( "xrate", 1024 * 1024 ); //< limit the transfer rate to 1MB/s (the file is 1GB big so the transfer will take 1024 seconds) - properties.Set( "cpTimeout", 10 ); //< timeout the job after 10 seconds - CPPUNIT_ASSERT_XRDST( process15.AddJob( properties, &results ) ); - CPPUNIT_ASSERT_XRDST( process15.Prepare() ); - CPPUNIT_ASSERT_XRDST_NOTOK( process15.Run(0), XrdCl::errOperationExpired ); - CPPUNIT_ASSERT_XRDST( fs.Rm( targetPath ) ); - - //-------------------------------------------------------------------------- - // Test posc for local files - //-------------------------------------------------------------------------- - results.Clear(); - properties.Clear(); - std::string localtrg = "file://localhost/data/tpcFile.dat"; - properties.Set( "source", sourceURL ); - properties.Set( "target", localtrg ); - properties.Set( "posc", true ); - CancelProgressHandler progress16; //> abort the copy after 100MB - CPPUNIT_ASSERT_XRDST( process16.AddJob( properties, &results ) ); - CPPUNIT_ASSERT_XRDST( process16.Prepare() ); - CPPUNIT_ASSERT_XRDST_NOTOK( process16.Run( &progress16 ), errOperationInterrupted ); - XrdCl::FileSystem localfs( "file://localhost" ); - XrdCl::StatInfo *ptr = nullptr; - CPPUNIT_ASSERT_XRDST_NOTOK( localfs.Stat( "/data/tpcFile.dat", ptr ), XrdCl::errLocalError ); - - //-------------------------------------------------------------------------- - // Test --retry and --retry-policy - //-------------------------------------------------------------------------- - results.Clear(); - properties.Clear(); - properties.Set( "xrate", 1024 * 1024 * 32 ); //< limit the transfer rate to 32MB/s - properties.Set( "cpTimeout", 20 ); //< timeout the job after 20 seconds - properties.Set( "source", sourceURL ); - properties.Set( "target", targetURL ); - env->PutInt( "CpRetry", 1 ); - env->PutString( "CpRetryPolicy", "continue" ); - CPPUNIT_ASSERT_XRDST( process17.AddJob( properties, &results ) ); - CPPUNIT_ASSERT_XRDST( process17.Prepare() ); - CPPUNIT_ASSERT_XRDST( process17.Run(0) ); - CPPUNIT_ASSERT_XRDST( fs.Rm( targetPath ) ); - env->PutInt( "CpRetry", XrdCl::DefaultCpRetry ); - env->PutString( "CpRetryPolicy", XrdCl::DefaultCpRetryPolicy ); - } - - //---------------------------------------------------------------------------- - // Copy from a Metalink - //---------------------------------------------------------------------------- - results.Clear(); - properties.Clear(); - properties.Set( "source", metalinkURL ); - properties.Set( "target", targetURL ); - properties.Set( "checkSumMode", "end2end" ); - properties.Set( "checkSumType", "zcrc32" ); - CPPUNIT_ASSERT_XRDST( process5.AddJob( properties, &results ) ); - CPPUNIT_ASSERT_XRDST( process5.Prepare() ); - CPPUNIT_ASSERT_XRDST( process5.Run(0) ); - CPPUNIT_ASSERT_XRDST( fs.Rm( targetPath ) ); - properties.Clear(); - - // XCp test - results.Clear(); - properties.Set( "source", xcpSourceURL ); - properties.Set( "target", targetURL ); - properties.Set( "checkSumMode", "end2end" ); - properties.Set( "checkSumType", "zcrc32" ); - properties.Set( "xcp", true ); - properties.Set( "nbXcpSources", 3 ); - CPPUNIT_ASSERT_XRDST( process7.AddJob( properties, &results ) ); - CPPUNIT_ASSERT_XRDST( process7.Prepare() ); - CPPUNIT_ASSERT_XRDST( process7.Run(0) ); - CPPUNIT_ASSERT_XRDST( fs.Rm( targetPath ) ); - properties.Clear(); - - //---------------------------------------------------------------------------- - // Copy to local fs - //---------------------------------------------------------------------------- - results.Clear(); - properties.Set( "source", sourceURL ); - properties.Set( "target", "file://localhost" + localFile ); - properties.Set( "checkSumMode", "end2end" ); - properties.Set( "checkSumType", "zcrc32" ); - CPPUNIT_ASSERT_XRDST( process8.AddJob( properties, &results ) ); - CPPUNIT_ASSERT_XRDST( process8.Prepare() ); - CPPUNIT_ASSERT_XRDST( process8.Run(0) ); - properties.Clear(); - - //---------------------------------------------------------------------------- - // Copy from local fs with extended attributes - //---------------------------------------------------------------------------- - - // set extended attributes in the local source file - File lf; - CPPUNIT_ASSERT_XRDST( lf.Open( "file://localhost" + localFile, OpenFlags::Write ) ); - std::vector attrs; attrs.push_back( xattr_t( "foo", "bar" ) ); - std::vector result; - CPPUNIT_ASSERT_XRDST( lf.SetXAttr( attrs, result ) ); - CPPUNIT_ASSERT( result.size() == 1 ); - CPPUNIT_ASSERT_XRDST( result.front().status ); - CPPUNIT_ASSERT_XRDST( lf.Close() ); - - results.Clear(); - properties.Set( "source", "file://localhost" + localFile ); - properties.Set( "target", targetURL ); - properties.Set( "checkSumMode", "end2end" ); - properties.Set( "checkSumType", "zcrc32" ); - properties.Set( "preserveXAttr", true ); - CPPUNIT_ASSERT_XRDST( process9.AddJob( properties, &results ) ); - CPPUNIT_ASSERT_XRDST( process9.Prepare() ); - CPPUNIT_ASSERT_XRDST( process9.Run(0) ); - properties.Clear(); - - // now test if the xattrs were preserved - std::vector xattrs; - CPPUNIT_ASSERT_XRDST( fs.ListXAttr( targetPath, xattrs ) ); - CPPUNIT_ASSERT( xattrs.size() == 1 ); - XAttr &xattr = xattrs.front(); - CPPUNIT_ASSERT_XRDST( xattr.status ); - CPPUNIT_ASSERT( xattr.name == "foo" && xattr.value == "bar" ); - - //---------------------------------------------------------------------------- - // Cleanup - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( fs.Rm( targetPath ) ); - CPPUNIT_ASSERT( remove( localFile.c_str() ) == 0 ); - - //---------------------------------------------------------------------------- - // Initialize and run the copy - //---------------------------------------------------------------------------- - properties.Set( "source", sourceURL ); - properties.Set( "target", targetURL ); - properties.Set( "checkSumMode", "end2end" ); - properties.Set( "checkSumType", "zcrc32" ); - if( thirdParty ) - properties.Set( "thirdParty", "only" ); - CPPUNIT_ASSERT_XRDST( process1.AddJob( properties, &results ) ); - CPPUNIT_ASSERT_XRDST( process1.Prepare() ); - CPPUNIT_ASSERT_XRDST( process1.Run(0) ); - CPPUNIT_ASSERT_XRDST( fs.Rm( targetPath ) ); - properties.Clear(); - - //---------------------------------------------------------------------------- - // Copy with `auto` checksum - //---------------------------------------------------------------------------- - results.Clear(); - properties.Set( "source", sourceURL ); - properties.Set( "target", targetURL ); - properties.Set( "checkSumMode", "end2end" ); - properties.Set( "checkSumType", "auto" ); - if( thirdParty ) - properties.Set( "thirdParty", "only" ); - CPPUNIT_ASSERT_XRDST( process11.AddJob( properties, &results ) ); - CPPUNIT_ASSERT_XRDST( process11.Prepare() ); - CPPUNIT_ASSERT_XRDST( process11.Run(0) ); - CPPUNIT_ASSERT_XRDST( fs.Rm( targetPath ) ); - properties.Clear(); - - // the further tests are only valid for third party copy for now - if( !thirdParty ) - return; - - //---------------------------------------------------------------------------- - // Abort the copy after 100MB - //---------------------------------------------------------------------------- -// CancelProgressHandler progress; -// CPPUNIT_ASSERT_XRDST( process2.AddJob( properties, &results ) ); -// CPPUNIT_ASSERT_XRDST( process2.Prepare() ); -// CPPUNIT_ASSERT_XRDST_NOTOK( process2.Run(&progress), errErrorResponse ); -// CPPUNIT_ASSERT_XRDST( fs.Rm( targetPath ) ); - - //---------------------------------------------------------------------------- - // Copy from a non-existent source - //---------------------------------------------------------------------------- - results.Clear(); - properties.Set( "source", "root://localhost:9999//test" ); - properties.Set( "target", targetURL ); - properties.Set( "initTimeout", 10 ); - properties.Set( "thirdParty", "only" ); - CPPUNIT_ASSERT_XRDST( process3.AddJob( properties, &results ) ); - CPPUNIT_ASSERT_XRDST( process3.Prepare() ); - XrdCl::XRootDStatus status = process3.Run(0); - CPPUNIT_ASSERT( !status.IsOK() && ( status.code == errOperationExpired || status.code == errConnectionError ) ); - - //---------------------------------------------------------------------------- - // Copy to a non-existent target - //---------------------------------------------------------------------------- - results.Clear(); - properties.Set( "source", sourceURL ); - properties.Set( "target", "root://localhost:9999//test" ); - properties.Set( "initTimeout", 10 ); - properties.Set( "thirdParty", "only" ); - CPPUNIT_ASSERT_XRDST( process4.AddJob( properties, &results ) ); - CPPUNIT_ASSERT_XRDST( process4.Prepare() ); - status = process4.Run(0); - CPPUNIT_ASSERT( !status.IsOK() && ( status.code == errOperationExpired || status.code == errConnectionError ) ); -} - -//------------------------------------------------------------------------------ -// Third party copy test -//------------------------------------------------------------------------------ -void FileCopyTest::ThirdPartyCopyTest() -{ - CopyTestFunc( true ); -} - -//------------------------------------------------------------------------------ -// Cormal copy test -//------------------------------------------------------------------------------ -void FileCopyTest::NormalCopyTest() -{ - CopyTestFunc( false ); -} diff --git a/tests/XrdClTests/FileSystemTest.cc b/tests/XrdClTests/FileSystemTest.cc deleted file mode 100644 index 80645a27e2f..00000000000 --- a/tests/XrdClTests/FileSystemTest.cc +++ /dev/null @@ -1,689 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2011-2012 by European Organization for Nuclear Research (CERN) -// Author: Lukasz Janyst -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -//------------------------------------------------------------------------------ - -#include -#include -#include -#include "XrdCl/XrdClDefaultEnv.hh" -#include "XrdCl/XrdClPlugInManager.hh" -#include "CppUnitXrdHelpers.hh" - -#include - -#include "TestEnv.hh" -#include "IdentityPlugIn.hh" - -using namespace XrdClTests; - -//------------------------------------------------------------------------------ -// Declaration -//------------------------------------------------------------------------------ -class FileSystemTest: public CppUnit::TestCase -{ - public: - CPPUNIT_TEST_SUITE( FileSystemTest ); - CPPUNIT_TEST( LocateTest ); - CPPUNIT_TEST( MvTest ); - CPPUNIT_TEST( ServerQueryTest ); - CPPUNIT_TEST( TruncateRmTest ); - CPPUNIT_TEST( MkdirRmdirTest ); - CPPUNIT_TEST( ChmodTest ); - CPPUNIT_TEST( PingTest ); - CPPUNIT_TEST( StatTest ); - CPPUNIT_TEST( StatVFSTest ); - CPPUNIT_TEST( ProtocolTest ); - CPPUNIT_TEST( DeepLocateTest ); - CPPUNIT_TEST( DirListTest ); - CPPUNIT_TEST( SendInfoTest ); - CPPUNIT_TEST( PrepareTest ); - CPPUNIT_TEST( XAttrTest ); - CPPUNIT_TEST( PlugInTest ); - CPPUNIT_TEST_SUITE_END(); - void LocateTest(); - void MvTest(); - void ServerQueryTest(); - void TruncateRmTest(); - void MkdirRmdirTest(); - void ChmodTest(); - void PingTest(); - void StatTest(); - void StatVFSTest(); - void ProtocolTest(); - void DeepLocateTest(); - void DirListTest(); - void SendInfoTest(); - void PrepareTest(); - void XAttrTest(); - void PlugInTest(); -}; - -CPPUNIT_TEST_SUITE_REGISTRATION( FileSystemTest ); - -//------------------------------------------------------------------------------ -// Locate test -//------------------------------------------------------------------------------ -void FileSystemTest::LocateTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Get the environment variables - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string remoteFile; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "RemoteFile", remoteFile ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - //---------------------------------------------------------------------------- - // Query the server for all of the file locations - //---------------------------------------------------------------------------- - FileSystem fs( url ); - - LocationInfo *locations = 0; - CPPUNIT_ASSERT_XRDST( fs.Locate( remoteFile, OpenFlags::Refresh, locations ) ); - CPPUNIT_ASSERT( locations ); - CPPUNIT_ASSERT( locations->GetSize() != 0 ); - delete locations; -} - -//------------------------------------------------------------------------------ -// Mv test -//------------------------------------------------------------------------------ -void FileSystemTest::MvTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Get the environment variables - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - std::string remoteFile; - - CPPUNIT_ASSERT( testEnv->GetString( "DiskServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "RemoteFile", remoteFile ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - std::string filePath1 = remoteFile; - std::string filePath2 = remoteFile + "2"; - - - LocationInfo *info = 0; - FileSystem fs( url ); - - // move the file - CPPUNIT_ASSERT_XRDST( fs.Mv( filePath1, filePath2 ) ); - // make sure it's not there - CPPUNIT_ASSERT_XRDST_NOTOK( fs.Locate( filePath1, OpenFlags::Refresh, info ), - errErrorResponse ); - // make sure the destination is there - CPPUNIT_ASSERT_XRDST( fs.Locate( filePath2, OpenFlags::Refresh, info ) ); - delete info; - // move it back - CPPUNIT_ASSERT_XRDST( fs.Mv( filePath2, filePath1 ) ); - // make sure it's there - CPPUNIT_ASSERT_XRDST( fs.Locate( filePath1, OpenFlags::Refresh, info ) ); - delete info; - // make sure the other one is gone - CPPUNIT_ASSERT_XRDST_NOTOK( fs.Locate( filePath2, OpenFlags::Refresh, info ), - errErrorResponse ); -} - -//------------------------------------------------------------------------------ -// Query test -//------------------------------------------------------------------------------ -void FileSystemTest::ServerQueryTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Get the environment variables - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string remoteFile; - - CPPUNIT_ASSERT( testEnv->GetString( "DiskServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "RemoteFile", remoteFile ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - FileSystem fs( url ); - Buffer *response = 0; - Buffer arg; - arg.FromString( remoteFile ); - CPPUNIT_ASSERT_XRDST( fs.Query( QueryCode::Checksum, arg, response ) ); - CPPUNIT_ASSERT( response ); - CPPUNIT_ASSERT( response->GetSize() != 0 ); - delete response; -} - -//------------------------------------------------------------------------------ -// Truncate/Rm test -//------------------------------------------------------------------------------ -void FileSystemTest::TruncateRmTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Get the environment variables - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "DataPath", dataPath ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - std::string filePath = dataPath + "/testfile"; - std::string fileUrl = address + "/"; - fileUrl += filePath; - - FileSystem fs( url ); - File f; - CPPUNIT_ASSERT_XRDST( f.Open( fileUrl, OpenFlags::Update | OpenFlags::Delete, - Access::UR | Access::UW ) ); - CPPUNIT_ASSERT_XRDST( fs.Truncate( filePath, 10000000 ) ); - CPPUNIT_ASSERT_XRDST( fs.Rm( filePath ) ); -} - -//------------------------------------------------------------------------------ -// Mkdir/Rmdir test -//------------------------------------------------------------------------------ -void FileSystemTest::MkdirRmdirTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Get the environment variables - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - - CPPUNIT_ASSERT( testEnv->GetString( "DiskServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "DataPath", dataPath ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - std::string dirPath1 = dataPath + "/testdir"; - std::string dirPath2 = dataPath + "/testdir/asdads"; - - FileSystem fs( url ); - - CPPUNIT_ASSERT_XRDST( fs.MkDir( dirPath2, MkDirFlags::MakePath, - Access::UR | Access::UW | Access::UX ) ); - CPPUNIT_ASSERT_XRDST( fs.RmDir( dirPath2 ) ); - CPPUNIT_ASSERT_XRDST( fs.RmDir( dirPath1 ) ); -} - -//------------------------------------------------------------------------------ -// Chmod test -//------------------------------------------------------------------------------ -void FileSystemTest::ChmodTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Get the environment variables - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - - CPPUNIT_ASSERT( testEnv->GetString( "DiskServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "DataPath", dataPath ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - std::string dirPath = dataPath + "/testdir"; - - FileSystem fs( url ); - - CPPUNIT_ASSERT_XRDST( fs.MkDir( dirPath, MkDirFlags::MakePath, - Access::UR | Access::UW | Access::UX ) ); - CPPUNIT_ASSERT_XRDST( fs.ChMod( dirPath, - Access::UR | Access::UW | Access::UX | - Access::GR | Access::GX ) ); - CPPUNIT_ASSERT_XRDST( fs.RmDir( dirPath ) ); -} - -//------------------------------------------------------------------------------ -// Locate test -//------------------------------------------------------------------------------ -void FileSystemTest::PingTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Get the environment variables - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - FileSystem fs( url ); - CPPUNIT_ASSERT_XRDST( fs.Ping() ); -} - -//------------------------------------------------------------------------------ -// Stat test -//------------------------------------------------------------------------------ -void FileSystemTest::StatTest() -{ - using namespace XrdCl; - - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string remoteFile; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "RemoteFile", remoteFile ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - FileSystem fs( url ); - StatInfo *response = 0; - CPPUNIT_ASSERT_XRDST( fs.Stat( remoteFile, response ) ); - CPPUNIT_ASSERT( response ); - CPPUNIT_ASSERT( response->GetSize() == 1048576000 ); - CPPUNIT_ASSERT( response->TestFlags( StatInfo::IsReadable ) ); - CPPUNIT_ASSERT( response->TestFlags( StatInfo::IsWritable ) ); - CPPUNIT_ASSERT( !response->TestFlags( StatInfo::IsDir ) ); - delete response; -} - -//------------------------------------------------------------------------------ -// Stat VFS test -//------------------------------------------------------------------------------ -void FileSystemTest::StatVFSTest() -{ - using namespace XrdCl; - - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "DataPath", dataPath ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - FileSystem fs( url ); - StatInfoVFS *response = 0; - CPPUNIT_ASSERT_XRDST( fs.StatVFS( dataPath, response ) ); - CPPUNIT_ASSERT( response ); - delete response; -} - -//------------------------------------------------------------------------------ -// Protocol test -//------------------------------------------------------------------------------ -void FileSystemTest::ProtocolTest() -{ - using namespace XrdCl; - - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - FileSystem fs( url ); - ProtocolInfo *response = 0; - CPPUNIT_ASSERT_XRDST( fs.Protocol( response ) ); - CPPUNIT_ASSERT( response ); - delete response; -} - -//------------------------------------------------------------------------------ -// Deep locate test -//------------------------------------------------------------------------------ -void FileSystemTest::DeepLocateTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Get the environment variables - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string remoteFile; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "RemoteFile", remoteFile ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - //---------------------------------------------------------------------------- - // Query the server for all of the file locations - //---------------------------------------------------------------------------- - FileSystem fs( url ); - - LocationInfo *locations = 0; - CPPUNIT_ASSERT_XRDST( fs.DeepLocate( remoteFile, OpenFlags::Refresh, locations ) ); - CPPUNIT_ASSERT( locations ); - CPPUNIT_ASSERT( locations->GetSize() != 0 ); - LocationInfo::Iterator it = locations->Begin(); - for( ; it != locations->End(); ++it ) - CPPUNIT_ASSERT( it->IsServer() ); - delete locations; -} - -//------------------------------------------------------------------------------ -// Dir list -//------------------------------------------------------------------------------ -void FileSystemTest::DirListTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Get the environment variables - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "DataPath", dataPath ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - std::string lsPath = dataPath + "/bigdir"; - - //---------------------------------------------------------------------------- - // Query the server for all of the file locations - //---------------------------------------------------------------------------- - FileSystem fs( url ); - - DirectoryList *list = 0; - CPPUNIT_ASSERT_XRDST( fs.DirList( lsPath, DirListFlags::Stat | DirListFlags::Locate, list ) ); - CPPUNIT_ASSERT( list ); - CPPUNIT_ASSERT( list->GetSize() == 40000 ); - - std::set dirls1; - for( auto itr = list->Begin(); itr != list->End(); ++itr ) - { - DirectoryList::ListEntry *entry = *itr; - dirls1.insert( entry->GetName() ); - } - - delete list; - list = 0; - - //---------------------------------------------------------------------------- - // Now do a chunked query - //---------------------------------------------------------------------------- - std::set dirls2; - - LocationInfo *info = 0; - CPPUNIT_ASSERT_XRDST( fs.DeepLocate( lsPath, OpenFlags::PrefName, info ) ); - CPPUNIT_ASSERT( info ); - - for( auto itr = info->Begin(); itr != info->End(); ++itr ) - { - XrdSysSemaphore sem( 0 ); - auto handler = XrdCl::ResponseHandler::Wrap( [&]( auto &s, auto &r ) - { - CPPUNIT_ASSERT_XRDST( s ); - auto &list = To( r ); - for( auto itr = list.Begin(); itr != list.End(); ++itr ) - dirls2.insert( ( *itr )->GetName() ); - if( s.code == XrdCl::suDone ) - sem.Post(); - } ); - - FileSystem fs1( std::string( itr->GetAddress() ) ); - CPPUNIT_ASSERT_XRDST( fs1.DirList( lsPath, DirListFlags::Stat | DirListFlags::Chunked, handler ) ); - sem.Wait(); - } - delete info; - info = 0; - - CPPUNIT_ASSERT( dirls1 == dirls2 ); - - //---------------------------------------------------------------------------- - // Now list an empty directory - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( fs.MkDir( "/data/empty", MkDirFlags::None, Access::None ) ); - CPPUNIT_ASSERT_XRDST( fs.DeepLocate( "/data/empty", OpenFlags::PrefName, info ) ); - CPPUNIT_ASSERT( info->GetSize() ); - FileSystem fs3( info->Begin()->GetAddress() ); - CPPUNIT_ASSERT_XRDST( fs3.DirList( "/data/empty", DirListFlags::Stat, list ) ); - CPPUNIT_ASSERT( list ); - CPPUNIT_ASSERT( list->GetSize() == 0 ); - CPPUNIT_ASSERT_XRDST( fs.RmDir( "/data/empty" ) ); - - delete list; - list = 0; - delete info; - info = 0; -} - - -//------------------------------------------------------------------------------ -// Set -//------------------------------------------------------------------------------ -void FileSystemTest::SendInfoTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Get the environment variables - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - FileSystem fs( url ); - - Buffer *id = 0; - CPPUNIT_ASSERT_XRDST( fs.SendInfo( "test stuff", id ) ); - CPPUNIT_ASSERT( id ); - CPPUNIT_ASSERT( id->GetSize() == 4 ); - delete id; -} - - -//------------------------------------------------------------------------------ -// Set -//------------------------------------------------------------------------------ -void FileSystemTest::PrepareTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Get the environment variables - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - FileSystem fs( url ); - - Buffer *id = 0; - std::vector list; - list.push_back( "/data/1db882c8-8cd6-4df1-941f-ce669bad3458.dat" ); - list.push_back( "/data/1db882c8-8cd6-4df1-941f-ce669bad3458.dat" ); - - CPPUNIT_ASSERT_XRDST( fs.Prepare( list, PrepareFlags::Stage, 1, id ) ); - CPPUNIT_ASSERT( id ); - CPPUNIT_ASSERT( id->GetSize() ); - delete id; -} - -//------------------------------------------------------------------------------ -// Extended attributes test -//------------------------------------------------------------------------------ -void FileSystemTest::XAttrTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Get the environment variables - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string remoteFile; - - CPPUNIT_ASSERT( testEnv->GetString( "DiskServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "RemoteFile", remoteFile ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - FileSystem fs( url ); - - std::map attributes - { - std::make_pair( "version", "v1.2.3-45" ), - std::make_pair( "checksum", "2ccc0e85556a6cd193dd8d2b40aab50c" ), - std::make_pair( "index", "4" ) - }; - - //---------------------------------------------------------------------------- - // Test SetXAttr - //---------------------------------------------------------------------------- - std::vector attrs; - auto itr1 = attributes.begin(); - for( ; itr1 != attributes.end() ; ++itr1 ) - attrs.push_back( std::make_tuple( itr1->first, itr1->second ) ); - - std::vector result1; - CPPUNIT_ASSERT_XRDST( fs.SetXAttr( remoteFile, attrs, result1 ) ); - - auto itr2 = result1.begin(); - for( ; itr2 != result1.end() ; ++itr2 ) - CPPUNIT_ASSERT_XRDST( itr2->status ); - result1.clear(); - - //---------------------------------------------------------------------------- - // Test GetXAttr - //---------------------------------------------------------------------------- - std::vector names; - itr1 = attributes.begin(); - for( ; itr1 != attributes.end() ; ++itr1 ) - names.push_back( itr1->first ); - - std::vector result2; - CPPUNIT_ASSERT_XRDST( fs.GetXAttr( remoteFile, names, result2 ) ); - - auto itr3 = result2.begin(); - for( ; itr3 != result2.end() ; ++itr3 ) - { - CPPUNIT_ASSERT_XRDST( itr3->status ); - auto match = attributes.find( itr3->name ); - CPPUNIT_ASSERT( match != attributes.end() ); - CPPUNIT_ASSERT( match->second == itr3->value ); - } - result2.clear(); - - //---------------------------------------------------------------------------- - // Test ListXAttr - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( fs.ListXAttr( remoteFile, result2 ) ); - - itr3 = result2.begin(); - for( ; itr3 != result2.end() ; ++itr3 ) - { - CPPUNIT_ASSERT_XRDST( itr3->status ); - auto match = attributes.find( itr3->name ); - CPPUNIT_ASSERT( match != attributes.end() ); - CPPUNIT_ASSERT( match->second == itr3->value ); - } - - result2.clear(); - - //---------------------------------------------------------------------------- - // Test DelXAttr - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( fs.DelXAttr( remoteFile, names, result1 ) ); - - itr2 = result1.begin(); - for( ; itr2 != result1.end() ; ++itr2 ) - CPPUNIT_ASSERT_XRDST( itr2->status ); - - result1.clear(); -} - -//------------------------------------------------------------------------------ -// Plug-in test -//------------------------------------------------------------------------------ -void FileSystemTest::PlugInTest() -{ - XrdCl::PlugInFactory *f = new IdentityFactory; - XrdCl::DefaultEnv::GetPlugInManager()->RegisterDefaultFactory(f); - LocateTest(); - MvTest(); - ServerQueryTest(); - TruncateRmTest(); - MkdirRmdirTest(); - ChmodTest(); - PingTest(); - StatTest(); - StatVFSTest(); - ProtocolTest(); - DeepLocateTest(); - DirListTest(); - SendInfoTest(); - PrepareTest(); - XrdCl::DefaultEnv::GetPlugInManager()->RegisterDefaultFactory(0); -} diff --git a/tests/XrdClTests/FileTest.cc b/tests/XrdClTests/FileTest.cc deleted file mode 100644 index 60eaf9fdda8..00000000000 --- a/tests/XrdClTests/FileTest.cc +++ /dev/null @@ -1,811 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2011-2012 by European Organization for Nuclear Research (CERN) -// Author: Lukasz Janyst -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -//------------------------------------------------------------------------------ - -#include -#include "TestEnv.hh" -#include "Utils.hh" -#include "IdentityPlugIn.hh" - -#include "CppUnitXrdHelpers.hh" -#include "XrdCl/XrdClFile.hh" -#include "XrdCl/XrdClDefaultEnv.hh" -#include "XrdCl/XrdClPlugInManager.hh" -#include "XrdCl/XrdClMessage.hh" -#include "XrdCl/XrdClSIDManager.hh" -#include "XrdCl/XrdClPostMaster.hh" -#include "XrdCl/XrdClXRootDTransport.hh" -#include "XrdCl/XrdClMessageUtils.hh" -#include "XrdCl/XrdClXRootDMsgHandler.hh" -#include "XrdCl/XrdClCopyProcess.hh" -#include "XrdCl/XrdClZipArchive.hh" -#include "XrdCl/XrdClConstants.hh" -#include "XrdCl/XrdClZipOperations.hh" - -using namespace XrdClTests; - -//------------------------------------------------------------------------------ -// Declaration -//------------------------------------------------------------------------------ -class FileTest: public CppUnit::TestCase -{ - public: - CPPUNIT_TEST_SUITE( FileTest ); - CPPUNIT_TEST( RedirectReturnTest ); - CPPUNIT_TEST( ReadTest ); - CPPUNIT_TEST( WriteTest ); - CPPUNIT_TEST( WriteVTest ); - CPPUNIT_TEST( VectorReadTest ); - CPPUNIT_TEST( VectorWriteTest ); - CPPUNIT_TEST( VirtualRedirectorTest ); - CPPUNIT_TEST( XAttrTest ); - CPPUNIT_TEST( PlugInTest ); - CPPUNIT_TEST_SUITE_END(); - void RedirectReturnTest(); - void ReadTest(); - void WriteTest(); - void WriteVTest(); - void VectorReadTest(); - void VectorWriteTest(); - void VirtualRedirectorTest(); - void XAttrTest(); - void PlugInTest(); -}; - -CPPUNIT_TEST_SUITE_REGISTRATION( FileTest ); - -//------------------------------------------------------------------------------ -// Redirect return test -//------------------------------------------------------------------------------ -void FileTest::RedirectReturnTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "DataPath", dataPath ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - std::string path = dataPath + "/cb4aacf1-6f28-42f2-b68a-90a73460f424.dat"; - std::string fileUrl = address + "/" + path; - - //---------------------------------------------------------------------------- - // Build the open request - //---------------------------------------------------------------------------- - Message *msg; - ClientOpenRequest *req; - MessageUtils::CreateRequest( msg, req, path.length() ); - req->requestid = kXR_open; - req->options = kXR_open_read | kXR_retstat; - req->dlen = path.length(); - msg->Append( path.c_str(), path.length(), 24 ); - XRootDTransport::SetDescription( msg ); - - SyncResponseHandler *handler = new SyncResponseHandler(); - MessageSendParams params; params.followRedirects = false; - MessageUtils::ProcessSendParams( params ); - OpenInfo *response = 0; - CPPUNIT_ASSERT_XRDST( MessageUtils::SendMessage( url, msg, handler, params, 0 ) ); - XRootDStatus st1 = MessageUtils::WaitForResponse( handler, response ); - delete handler; - CPPUNIT_ASSERT_XRDST_NOTOK( st1, errRedirect ); - CPPUNIT_ASSERT( !response ); - delete response; -} - -//------------------------------------------------------------------------------ -// Read test -//------------------------------------------------------------------------------ -void FileTest::ReadTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "DataPath", dataPath ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - std::string filePath = dataPath + "/cb4aacf1-6f28-42f2-b68a-90a73460f424.dat"; - std::string fileUrl = address + "/"; - fileUrl += filePath; - - //---------------------------------------------------------------------------- - // Fetch some data and checksum - //---------------------------------------------------------------------------- - const uint32_t MB = 1024*1024; - char *buffer1 = new char[40*MB]; - char *buffer2 = new char[40*MB]; - uint32_t bytesRead1 = 0; - uint32_t bytesRead2 = 0; - File f; - StatInfo *stat; - - //---------------------------------------------------------------------------- - // Open the file - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( f.Open( fileUrl, OpenFlags::Read ) ); - - //---------------------------------------------------------------------------- - // Stat1 - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( f.Stat( false, stat ) ); - CPPUNIT_ASSERT( stat ); - CPPUNIT_ASSERT( stat->GetSize() == 1048576000 ); - CPPUNIT_ASSERT( stat->TestFlags( StatInfo::IsReadable ) ); - delete stat; - stat = 0; - - //---------------------------------------------------------------------------- - // Stat2 - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( f.Stat( true, stat ) ); - CPPUNIT_ASSERT( stat ); - CPPUNIT_ASSERT( stat->GetSize() == 1048576000 ); - CPPUNIT_ASSERT( stat->TestFlags( StatInfo::IsReadable ) ); - delete stat; - - //---------------------------------------------------------------------------- - // Read test - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( f.Read( 10*MB, 40*MB, buffer1, bytesRead1 ) ); - CPPUNIT_ASSERT_XRDST( f.Read( 1008576000, 40*MB, buffer2, bytesRead2 ) ); - CPPUNIT_ASSERT( bytesRead1 == 40*MB ); - CPPUNIT_ASSERT( bytesRead2 == 40000000 ); - - uint32_t crc = XrdClTests::Utils::ComputeCRC32( buffer1, 40*MB ); - CPPUNIT_ASSERT( crc == 3303853367UL ); - - crc = XrdClTests::Utils::ComputeCRC32( buffer2, 40000000 ); - CPPUNIT_ASSERT( crc == 898701504UL ); - - delete [] buffer1; - delete [] buffer2; - - CPPUNIT_ASSERT_XRDST( f.Close() ); - - //---------------------------------------------------------------------------- - // Read ZIP archive test (uncompressed) - //---------------------------------------------------------------------------- - std::string archiveUrl = address + "/" + dataPath + "/data.zip"; - - ZipArchive zip; - CPPUNIT_ASSERT_XRDST( WaitFor( OpenArchive( zip, archiveUrl, OpenFlags::Read ) ) ); - - //---------------------------------------------------------------------------- - // There are 3 files in the data.zip archive: - // - athena.log - // - paper.txt - // - EastAsianWidth.txt - //---------------------------------------------------------------------------- - - struct - { - std::string file; // file name - uint64_t offset; // offset in the file - uint32_t size; // number of characters to be read - char buffer[100]; // the buffer - std::string expected; // expected result - } testset[] = - { - { "athena.log", 65530, 99, {0}, "D__Jet" }, // reads past the end of the file (there are just 6 characters to read not 99) - { "paper.txt", 1024, 65, {0}, "igh rate (the order of 100 kHz), the data are usually distributed" }, - { "EastAsianWidth.txt", 2048, 18, {0}, "8;Na # DIGIT EIGHT" } - }; - - for( int i = 0; i < 3; ++i ) - { - std::string result; - CPPUNIT_ASSERT_XRDST( WaitFor( - ReadFrom( zip, testset[i].file, testset[i].offset, testset[i].size, testset[i].buffer ) >> - [&result]( auto& s, auto& c ) - { - if( s.IsOK() ) - result.assign( static_cast(c.buffer), c.length ); - } - ) ); - CPPUNIT_ASSERT( testset[i].expected == result ); - } - - CPPUNIT_ASSERT_XRDST( WaitFor( CloseArchive( zip ) ) ); -} - - -//------------------------------------------------------------------------------ -// Read test -//------------------------------------------------------------------------------ -void FileTest::WriteTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "DataPath", dataPath ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - std::string filePath = dataPath + "/testFile.dat"; - std::string fileUrl = address + "/"; - fileUrl += filePath; - - //---------------------------------------------------------------------------- - // Fetch some data and checksum - //---------------------------------------------------------------------------- - const uint32_t MB = 1024*1024; - char *buffer1 = new char[4*MB]; - char *buffer2 = new char[4*MB]; - char *buffer3 = new char[4*MB]; - char *buffer4 = new char[4*MB]; - uint32_t bytesRead1 = 0; - uint32_t bytesRead2 = 0; - File f1, f2; - - CPPUNIT_ASSERT( XrdClTests::Utils::GetRandomBytes( buffer1, 4*MB ) == 4*MB ); - CPPUNIT_ASSERT( XrdClTests::Utils::GetRandomBytes( buffer2, 4*MB ) == 4*MB ); - uint32_t crc1 = XrdClTests::Utils::ComputeCRC32( buffer1, 4*MB ); - crc1 = XrdClTests::Utils::UpdateCRC32( crc1, buffer2, 4*MB ); - - //---------------------------------------------------------------------------- - // Write the data - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT( f1.Open( fileUrl, OpenFlags::Delete | OpenFlags::Update, - Access::UR | Access::UW ).IsOK() ); - CPPUNIT_ASSERT( f1.Write( 0, 4*MB, buffer1 ).IsOK() ); - CPPUNIT_ASSERT( f1.Write( 4*MB, 4*MB, buffer2 ).IsOK() ); - CPPUNIT_ASSERT( f1.Sync().IsOK() ); - CPPUNIT_ASSERT( f1.Close().IsOK() ); - - //---------------------------------------------------------------------------- - // Read the data and verify the checksums - //---------------------------------------------------------------------------- - StatInfo *stat = 0; - CPPUNIT_ASSERT( f2.Open( fileUrl, OpenFlags::Read ).IsOK() ); - CPPUNIT_ASSERT( f2.Stat( false, stat ).IsOK() ); - CPPUNIT_ASSERT( stat ); - CPPUNIT_ASSERT( stat->GetSize() == 8*MB ); - CPPUNIT_ASSERT( f2.Read( 0, 4*MB, buffer3, bytesRead1 ).IsOK() ); - CPPUNIT_ASSERT( f2.Read( 4*MB, 4*MB, buffer4, bytesRead2 ).IsOK() ); - CPPUNIT_ASSERT( bytesRead1 == 4*MB ); - CPPUNIT_ASSERT( bytesRead2 == 4*MB ); - uint32_t crc2 = XrdClTests::Utils::ComputeCRC32( buffer3, 4*MB ); - crc2 = XrdClTests::Utils::UpdateCRC32( crc2, buffer4, 4*MB ); - CPPUNIT_ASSERT( f2.Close().IsOK() ); - CPPUNIT_ASSERT( crc1 == crc2 ); - - //---------------------------------------------------------------------------- - // Truncate test - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT( f1.Open( fileUrl, OpenFlags::Delete | OpenFlags::Update, - Access::UR | Access::UW ).IsOK() ); - CPPUNIT_ASSERT( f1.Truncate( 20*MB ).IsOK() ); - CPPUNIT_ASSERT( f1.Close().IsOK() ); - FileSystem fs( url ); - StatInfo *response = 0; - CPPUNIT_ASSERT( fs.Stat( filePath, response ).IsOK() ); - CPPUNIT_ASSERT( response ); - CPPUNIT_ASSERT( response->GetSize() == 20*MB ); - CPPUNIT_ASSERT( fs.Rm( filePath ).IsOK() ); - delete [] buffer1; - delete [] buffer2; - delete [] buffer3; - delete [] buffer4; - delete response; - delete stat; -} - -//------------------------------------------------------------------------------ -// WriteV test -//------------------------------------------------------------------------------ -void FileTest::WriteVTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "DataPath", dataPath ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - std::string filePath = dataPath + "/testFile.dat"; - std::string fileUrl = address + "/"; - fileUrl += filePath; - - //---------------------------------------------------------------------------- - // Fetch some data and checksum - //---------------------------------------------------------------------------- - const uint32_t MB = 1024*1024; - char *buffer1 = new char[4*MB]; - char *buffer2 = new char[4*MB]; - char *buffer3 = new char[8*MB]; - uint32_t bytesRead1 = 0; - File f1, f2; - - CPPUNIT_ASSERT( XrdClTests::Utils::GetRandomBytes( buffer1, 4*MB ) == 4*MB ); - CPPUNIT_ASSERT( XrdClTests::Utils::GetRandomBytes( buffer2, 4*MB ) == 4*MB ); - uint32_t crc1 = XrdClTests::Utils::ComputeCRC32( buffer1, 4*MB ); - crc1 = XrdClTests::Utils::UpdateCRC32( crc1, buffer2, 4*MB ); - - //---------------------------------------------------------------------------- - // Prepare IO vector - //---------------------------------------------------------------------------- - int iovcnt = 2; - iovec iov[iovcnt]; - iov[0].iov_base = buffer1; - iov[0].iov_len = 4*MB; - iov[1].iov_base = buffer2; - iov[1].iov_len = 4*MB; - - //---------------------------------------------------------------------------- - // Write the data - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT( f1.Open( fileUrl, OpenFlags::Delete | OpenFlags::Update, - Access::UR | Access::UW ).IsOK() ); - CPPUNIT_ASSERT( f1.WriteV( 0, iov, iovcnt ).IsOK() ); - CPPUNIT_ASSERT( f1.Sync().IsOK() ); - CPPUNIT_ASSERT( f1.Close().IsOK() ); - - //---------------------------------------------------------------------------- - // Read the data and verify the checksums - //---------------------------------------------------------------------------- - StatInfo *stat = 0; - CPPUNIT_ASSERT( f2.Open( fileUrl, OpenFlags::Read ).IsOK() ); - CPPUNIT_ASSERT( f2.Stat( false, stat ).IsOK() ); - CPPUNIT_ASSERT( stat ); - CPPUNIT_ASSERT( stat->GetSize() == 8*MB ); - CPPUNIT_ASSERT( f2.Read( 0, 8*MB, buffer3, bytesRead1 ).IsOK() ); - CPPUNIT_ASSERT( bytesRead1 == 8*MB ); - - uint32_t crc2 = XrdClTests::Utils::ComputeCRC32( buffer3, 8*MB ); - CPPUNIT_ASSERT( f2.Close().IsOK() ); - CPPUNIT_ASSERT( crc1 == crc2 ); - - FileSystem fs( url ); - CPPUNIT_ASSERT( fs.Rm( filePath ).IsOK() ); - delete [] buffer1; - delete [] buffer2; - delete [] buffer3; - delete stat; -} - -//------------------------------------------------------------------------------ -// Vector read test -//------------------------------------------------------------------------------ -void FileTest::VectorReadTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "DataPath", dataPath ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - std::string filePath = dataPath + "/a048e67f-4397-4bb8-85eb-8d7e40d90763.dat"; - std::string fileUrl = address + "/"; - fileUrl += filePath; - - //---------------------------------------------------------------------------- - // Fetch some data and checksum - //---------------------------------------------------------------------------- - const uint32_t MB = 1024*1024; - char *buffer1 = new char[40*MB]; - char *buffer2 = new char[40*256000]; - File f; - - //---------------------------------------------------------------------------- - // Build the chunk list - //---------------------------------------------------------------------------- - ChunkList chunkList1; - ChunkList chunkList2; - for( int i = 0; i < 40; ++i ) - { - chunkList1.push_back( ChunkInfo( (i+1)*10*MB, 1*MB ) ); - chunkList2.push_back( ChunkInfo( (i+1)*10*MB, 256000 ) ); - } - - //---------------------------------------------------------------------------- - // Open the file - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( f.Open( fileUrl, OpenFlags::Read ) ); - VectorReadInfo *info = 0; - CPPUNIT_ASSERT_XRDST( f.VectorRead( chunkList1, buffer1, info ) ); - CPPUNIT_ASSERT( info->GetSize() == 40*MB ); - delete info; - uint32_t crc = 0; - crc = XrdClTests::Utils::ComputeCRC32( buffer1, 40*MB ); - CPPUNIT_ASSERT( crc == 3695956670UL ); - - info = 0; - CPPUNIT_ASSERT_XRDST( f.VectorRead( chunkList2, buffer2, info ) ); - CPPUNIT_ASSERT( info->GetSize() == 40*256000 ); - delete info; - crc = XrdClTests::Utils::ComputeCRC32( buffer2, 40*256000 ); - CPPUNIT_ASSERT( crc == 3492603530UL ); - - CPPUNIT_ASSERT_XRDST( f.Close() ); - - delete [] buffer1; - delete [] buffer2; -} - -void gen_random_str(char *s, const int len) -{ - static const char alphanum[] = - "0123456789" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz"; - - for (int i = 0; i < len - 1; ++i) - { - s[i] = alphanum[rand() % (sizeof(alphanum) - 1)]; - } - - s[len - 1] = 0; -} - -//------------------------------------------------------------------------------ -// Vector write test -//------------------------------------------------------------------------------ -void FileTest::VectorWriteTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "DataPath", dataPath ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - std::string filePath = dataPath + "/a048e67f-4397-4bb8-85eb-8d7e40d90763.dat"; - std::string fileUrl = address + "/"; - fileUrl += filePath; - - //---------------------------------------------------------------------------- - // Build a random chunk list for vector write - //---------------------------------------------------------------------------- - - const uint32_t MB = 1024*1024; - const uint32_t GB = 1000*MB; // maybe that's not 100% precise but that's - // what we have in our testbed - - time_t seed = time( 0 ); - srand( seed ); - DefaultEnv::GetLog()->Info( UtilityMsg, - "Carrying out the VectorWrite test with seed: %d", seed ); - - // figure out how many chunks are we going to write/read - size_t nbChunks = rand() % 100 + 1; - - XrdCl::ChunkList chunks; - size_t min_offset = 0; - uint32_t expectedCrc32 = 0; - size_t totalSize = 0; - - for( size_t i = 0; i < nbChunks; ++i ) - { - // figure out the offset - size_t offset = min_offset + rand() % ( GB - min_offset + 1 ); - - // figure out the size - size_t size = MB + rand() % ( MB + 1 ); - if( offset + size >= GB ) - size = GB - offset; - - // generate random string of given size - char *buffer = new char[size]; - gen_random_str( buffer, size ); - - // calculate expected checksum - expectedCrc32 = XrdClTests::Utils::UpdateCRC32( expectedCrc32, buffer, size ); - totalSize += size; - chunks.push_back( XrdCl::ChunkInfo( offset, size, buffer ) ); - - min_offset = offset + size; - if( min_offset >= GB ) - break; - } - - //---------------------------------------------------------------------------- - // Open the file - //---------------------------------------------------------------------------- - File f; - CPPUNIT_ASSERT_XRDST( f.Open( fileUrl, OpenFlags::Update ) ); - - //---------------------------------------------------------------------------- - // First do a VectorRead so we can revert to the original state - //---------------------------------------------------------------------------- - char *buffer1 = new char[totalSize]; - VectorReadInfo *info1 = 0; - CPPUNIT_ASSERT_XRDST( f.VectorRead( chunks, buffer1, info1 ) ); - - //---------------------------------------------------------------------------- - // Then do the VectorWrite - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( f.VectorWrite( chunks ) ); - - //---------------------------------------------------------------------------- - // Now do a vector read and verify that the checksum is the same - //---------------------------------------------------------------------------- - char *buffer2 = new char[totalSize]; - VectorReadInfo *info2 = 0; - CPPUNIT_ASSERT_XRDST( f.VectorRead( chunks, buffer2, info2 ) ); - - CPPUNIT_ASSERT( info2->GetSize() == totalSize ); - uint32_t crc32 = XrdClTests::Utils::ComputeCRC32( buffer2, totalSize ); - CPPUNIT_ASSERT( crc32 == expectedCrc32 ); - - //---------------------------------------------------------------------------- - // And finally revert to the original state - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( f.VectorWrite( info1->GetChunks() ) ); - CPPUNIT_ASSERT_XRDST( f.Close() ); - - delete info1; - delete info2; - delete [] buffer1; - delete [] buffer2; - for( auto itr = chunks.begin(); itr != chunks.end(); ++itr ) - delete[] (char*)itr->buffer; -} - -void FileTest::VirtualRedirectorTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "DataPath", dataPath ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - std::string mlUrl1 = address + "/" + dataPath + "/metalink/mlFileTest1.meta4"; - std::string mlUrl2 = address + "/" + dataPath + "/metalink/mlFileTest2.meta4"; - std::string mlUrl3 = address + "/" + dataPath + "/metalink/mlFileTest3.meta4"; - std::string mlUrl4 = address + "/" + dataPath + "/metalink/mlFileTest4.meta4"; - - File f1, f2, f3, f4; - - const std::string fileUrl = "root://srv1:1094//data/a048e67f-4397-4bb8-85eb-8d7e40d90763.dat"; - const std::string key = "LastURL"; - std::string value; - - //---------------------------------------------------------------------------- - // Open the 1st metalink file - // (the metalink contains just one file with a correct location) - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( f1.Open( mlUrl1, OpenFlags::Read ) ); - CPPUNIT_ASSERT( f1.GetProperty( key, value ) ); - URL lastUrl( value ); - CPPUNIT_ASSERT( lastUrl.GetLocation() == fileUrl ); - CPPUNIT_ASSERT_XRDST( f1.Close() ); - - //---------------------------------------------------------------------------- - // Open the 2nd metalink file - // (the metalink contains 2 files, the one with higher priority does not exist) - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( f2.Open( mlUrl2, OpenFlags::Read ) ); - CPPUNIT_ASSERT( f2.GetProperty( key, value ) ); - URL lastUrl2( value ); - CPPUNIT_ASSERT( lastUrl2.GetLocation() == fileUrl ); - CPPUNIT_ASSERT_XRDST( f2.Close() ); - - //---------------------------------------------------------------------------- - // Open the 3rd metalink file - // (the metalink contains 2 files, both don't exist) - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST_NOTOK( f3.Open( mlUrl3, OpenFlags::Read ), errErrorResponse ); - - //---------------------------------------------------------------------------- - // Open the 4th metalink file - // (the metalink contains 2 files, both exist) - //---------------------------------------------------------------------------- - const std::string replica1 = "root://srv3:1094//data/3c9a9dd8-bc75-422c-b12c-f00604486cc1.dat"; - const std::string replica2 = "root://srv2:1094//data/3c9a9dd8-bc75-422c-b12c-f00604486cc1.dat"; - - CPPUNIT_ASSERT_XRDST( f4.Open( mlUrl4, OpenFlags::Read ) ); - CPPUNIT_ASSERT( f4.GetProperty( key, value ) ); - URL lastUrl3( value ); - CPPUNIT_ASSERT( lastUrl3.GetLocation() == replica1 ); - CPPUNIT_ASSERT_XRDST( f4.Close() ); - //---------------------------------------------------------------------------- - // Delete the replica that has been selected by the virtual redirector - //---------------------------------------------------------------------------- - FileSystem fs( replica1 ); - CPPUNIT_ASSERT_XRDST( fs.Rm( "/data/3c9a9dd8-bc75-422c-b12c-f00604486cc1.dat" ) ); - //---------------------------------------------------------------------------- - // Now reopen the file - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( f4.Open( mlUrl4, OpenFlags::Read ) ); - CPPUNIT_ASSERT( f4.GetProperty( key, value ) ); - URL lastUrl4( value ); - CPPUNIT_ASSERT( lastUrl4.GetLocation() == replica2 ); - CPPUNIT_ASSERT_XRDST( f4.Close() ); - //---------------------------------------------------------------------------- - // Recreate the deleted file - //---------------------------------------------------------------------------- - CopyProcess process; - PropertyList properties, results; - properties.Set( "source", replica2 ); - properties.Set( "target", replica1 ); - CPPUNIT_ASSERT_XRDST( process.AddJob( properties, &results ) ); - CPPUNIT_ASSERT_XRDST( process.Prepare() ); - CPPUNIT_ASSERT_XRDST( process.Run(0) ); -} - -void FileTest::XAttrTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Get the environment variables - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - - CPPUNIT_ASSERT( testEnv->GetString( "DiskServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "DataPath", dataPath ) ); - - std::string filePath = dataPath + "/a048e67f-4397-4bb8-85eb-8d7e40d90763.dat"; - std::string fileUrl = address + "/" + filePath; - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - - File file; - CPPUNIT_ASSERT_XRDST( file.Open( fileUrl, OpenFlags::Update ) ); - - std::map attributes - { - std::make_pair( "version", "v1.2.3-45" ), - std::make_pair( "checksum", "2ccc0e85556a6cd193dd8d2b40aab50c" ), - std::make_pair( "index", "4" ) - }; - - //---------------------------------------------------------------------------- - // Test SetXAttr - //---------------------------------------------------------------------------- - std::vector attrs; - auto itr1 = attributes.begin(); - for( ; itr1 != attributes.end() ; ++itr1 ) - attrs.push_back( std::make_tuple( itr1->first, itr1->second ) ); - - std::vector result1; - CPPUNIT_ASSERT_XRDST( file.SetXAttr( attrs, result1 ) ); - - auto itr2 = result1.begin(); - for( ; itr2 != result1.end() ; ++itr2 ) - CPPUNIT_ASSERT_XRDST( itr2->status ); - - //---------------------------------------------------------------------------- - // Test GetXAttr - //---------------------------------------------------------------------------- - std::vector names; - itr1 = attributes.begin(); - for( ; itr1 != attributes.end() ; ++itr1 ) - names.push_back( itr1->first ); - - std::vector result2; - CPPUNIT_ASSERT_XRDST( file.GetXAttr( names, result2 ) ); - - auto itr3 = result2.begin(); - for( ; itr3 != result2.end() ; ++itr3 ) - { - CPPUNIT_ASSERT_XRDST( itr3->status ); - auto match = attributes.find( itr3->name ); - CPPUNIT_ASSERT( match != attributes.end() ); - CPPUNIT_ASSERT( match->second == itr3->value ); - } - - //---------------------------------------------------------------------------- - // Test ListXAttr - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( file.ListXAttr( result2 ) ); - - itr3 = result2.begin(); - for( ; itr3 != result2.end() ; ++itr3 ) - { - CPPUNIT_ASSERT_XRDST( itr3->status ); - auto match = attributes.find( itr3->name ); - CPPUNIT_ASSERT( match != attributes.end() ); - CPPUNIT_ASSERT( match->second == itr3->value ); - } - - //---------------------------------------------------------------------------- - // Test DelXAttr - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( file.DelXAttr( names, result1 ) ); - - itr2 = result1.begin(); - for( ; itr2 != result1.end() ; ++itr2 ) - CPPUNIT_ASSERT_XRDST( itr2->status ); - - CPPUNIT_ASSERT_XRDST( file.Close() ); -} - -//------------------------------------------------------------------------------ -// Plug-in test -//------------------------------------------------------------------------------ -void FileTest::PlugInTest() -{ - XrdCl::PlugInFactory *f = new IdentityFactory; - XrdCl::DefaultEnv::GetPlugInManager()->RegisterDefaultFactory(f); - RedirectReturnTest(); - ReadTest(); - WriteTest(); - VectorReadTest(); - XrdCl::DefaultEnv::GetPlugInManager()->RegisterDefaultFactory(0); -} diff --git a/tests/XrdClTests/IdentityPlugIn.cc b/tests/XrdClTests/IdentityPlugIn.cc deleted file mode 100644 index 173c42ce347..00000000000 --- a/tests/XrdClTests/IdentityPlugIn.cc +++ /dev/null @@ -1,488 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2014 by European Organization for Nuclear Research (CERN) -// Author: Lukasz Janyst -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -// -// In applying this licence, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -//------------------------------------------------------------------------------ - -#include "XrdCl/XrdClFile.hh" -#include "XrdCl/XrdClFileSystem.hh" -#include "XrdCl/XrdClPlugInInterface.hh" -#include "XrdCl/XrdClLog.hh" -#include "IdentityPlugIn.hh" -#include "TestEnv.hh" - -using namespace XrdCl; -using namespace XrdClTests; - -namespace -{ - //---------------------------------------------------------------------------- - // A plugin that forwards all the calls to XrdCl::File - //---------------------------------------------------------------------------- - class IdentityFile: public XrdCl::FilePlugIn - { - public: - //------------------------------------------------------------------------ - // Constructor - //------------------------------------------------------------------------ - IdentityFile() - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFile::IdentityFile" ); - pFile = new File( false ); - } - - //------------------------------------------------------------------------ - // Destructor - //------------------------------------------------------------------------ - virtual ~IdentityFile() - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFile::~IdentityFile" ); - delete pFile; - } - - //------------------------------------------------------------------------ - // Open - //------------------------------------------------------------------------ - virtual XRootDStatus Open( const std::string &url, - OpenFlags::Flags flags, - Access::Mode mode, - ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFile::Open" ); - return pFile->Open( url, flags, mode, handler, timeout ); - } - - //------------------------------------------------------------------------ - // Close - //------------------------------------------------------------------------ - virtual XRootDStatus Close( ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFile::Close" ); - return pFile->Close( handler, timeout ); - } - - //------------------------------------------------------------------------ - // Stat - //------------------------------------------------------------------------ - virtual XRootDStatus Stat( bool force, - ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFile::Stat" ); - return pFile->Stat( force, handler, timeout ); - } - - - //------------------------------------------------------------------------ - // Read - //------------------------------------------------------------------------ - virtual XRootDStatus Read( uint64_t offset, - uint32_t size, - void *buffer, - ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFile::Read" ); - return pFile->Read( offset, size, buffer, handler, timeout ); - } - - //------------------------------------------------------------------------ - // Write - //------------------------------------------------------------------------ - virtual XRootDStatus Write( uint64_t offset, - uint32_t size, - const void *buffer, - ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFile::Write" ); - return pFile->Write( offset, size, buffer, handler, timeout ); - } - - //------------------------------------------------------------------------ - // Sync - //------------------------------------------------------------------------ - virtual XRootDStatus Sync( ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFile::Sync" ); - return pFile->Sync( handler, timeout ); - } - - //------------------------------------------------------------------------ - // Truncate - //------------------------------------------------------------------------ - virtual XRootDStatus Truncate( uint64_t size, - ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFile::Truncate" ); - return pFile->Truncate( size, handler, timeout ); - } - - //------------------------------------------------------------------------ - // VectorRead - //------------------------------------------------------------------------ - virtual XRootDStatus VectorRead( const ChunkList &chunks, - void *buffer, - ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFile::VectorRead" ); - return pFile->VectorRead( chunks, buffer, handler, timeout ); - } - - //------------------------------------------------------------------------ - // Fcntl - //------------------------------------------------------------------------ - virtual XRootDStatus Fcntl( const Buffer &arg, - ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFile::Fcntl" ); - return pFile->Fcntl( arg, handler, timeout ); - } - - //------------------------------------------------------------------------ - // Visa - //------------------------------------------------------------------------ - virtual XRootDStatus Visa( ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFile::Visa" ); - return pFile->Visa( handler, timeout ); - } - - //------------------------------------------------------------------------ - // IsOpen - //------------------------------------------------------------------------ - virtual bool IsOpen() const - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFile::IsOpen" ); - return pFile->IsOpen(); - } - - //------------------------------------------------------------------------ - // SetProperty - //------------------------------------------------------------------------ - virtual bool SetProperty( const std::string &name, - const std::string &value ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFile::SetProperty" ); - return pFile->SetProperty( name, value ); - } - - //------------------------------------------------------------------------ - // GetProperty - //------------------------------------------------------------------------ - virtual bool GetProperty( const std::string &name, - std::string &value ) const - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFile::GetProperty" ); - return pFile->GetProperty( name, value ); - } - - private: - XrdCl::File *pFile; - }; - - //---------------------------------------------------------------------------- - // A plug-in that forwards all the calls to a XrdCl::FileSystem object - //---------------------------------------------------------------------------- - class IdentityFileSystem: public FileSystemPlugIn - { - public: - //------------------------------------------------------------------------ - // Constructor - //------------------------------------------------------------------------ - IdentityFileSystem( const std::string &url ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFileSystem::IdentityFileSystem" ); - pFileSystem = new XrdCl::FileSystem( URL(url), false ); - } - - //------------------------------------------------------------------------ - // Destructor - //------------------------------------------------------------------------ - virtual ~IdentityFileSystem() - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFileSystem::~IdentityFileSysytem" ); - delete pFileSystem; - } - - //------------------------------------------------------------------------ - // Locate - //------------------------------------------------------------------------ - virtual XRootDStatus Locate( const std::string &path, - OpenFlags::Flags flags, - ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFileSystem::Locate" ); - return pFileSystem->Locate( path, flags, handler, timeout ); - } - - //------------------------------------------------------------------------ - // Mv - //------------------------------------------------------------------------ - virtual XRootDStatus Mv( const std::string &source, - const std::string &dest, - ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFileSystem::Mv" ); - return pFileSystem->Mv( source, dest, handler, timeout ); - } - - //------------------------------------------------------------------------ - // Query - //------------------------------------------------------------------------ - virtual XRootDStatus Query( QueryCode::Code queryCode, - const Buffer &arg, - ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFileSystem::Query" ); - return pFileSystem->Query( queryCode, arg, handler, timeout ); - } - - //------------------------------------------------------------------------ - // Truncate - //------------------------------------------------------------------------ - virtual XRootDStatus Truncate( const std::string &path, - uint64_t size, - ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFileSystem::Truncate" ); - return pFileSystem->Truncate( path, size, handler, timeout ); - } - - //------------------------------------------------------------------------ - // Rm - //------------------------------------------------------------------------ - virtual XRootDStatus Rm( const std::string &path, - ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFileSystem::Rm" ); - return pFileSystem->Rm( path, handler, timeout ); - } - - //------------------------------------------------------------------------ - // MkDir - //------------------------------------------------------------------------ - virtual XRootDStatus MkDir( const std::string &path, - MkDirFlags::Flags flags, - Access::Mode mode, - ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFileSystem::MkDir" ); - return pFileSystem->MkDir( path, flags, mode, handler, timeout ); - } - - //------------------------------------------------------------------------ - // RmDir - //------------------------------------------------------------------------ - virtual XRootDStatus RmDir( const std::string &path, - ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFileSystem::RmDir" ); - return pFileSystem->RmDir( path, handler, timeout ); - } - - //------------------------------------------------------------------------ - // ChMod - //------------------------------------------------------------------------ - virtual XRootDStatus ChMod( const std::string &path, - Access::Mode mode, - ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFileSystem::ChMod" ); - return pFileSystem->ChMod( path, mode, handler, timeout ); - } - - //------------------------------------------------------------------------ - // Ping - //------------------------------------------------------------------------ - virtual XRootDStatus Ping( ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFileSystem::Ping" ); - return pFileSystem->Ping( handler, timeout ); - } - - //------------------------------------------------------------------------ - // Stat - //------------------------------------------------------------------------ - virtual XRootDStatus Stat( const std::string &path, - ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFileSystem::Stat" ); - return pFileSystem->Stat( path, handler, timeout ); - } - - //------------------------------------------------------------------------ - // StatVFS - //------------------------------------------------------------------------ - virtual XRootDStatus StatVFS( const std::string &path, - ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFileSystem::StatVFS" ); - return pFileSystem->StatVFS( path, handler, timeout ); - } - - //------------------------------------------------------------------------ - // Protocol - //------------------------------------------------------------------------ - virtual XRootDStatus Protocol( ResponseHandler *handler, - uint16_t timeout = 0 ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFileSystem::Protocol" ); - return pFileSystem->Protocol( handler, timeout ); - } - - //------------------------------------------------------------------------ - // DirlList - //------------------------------------------------------------------------ - virtual XRootDStatus DirList( const std::string &path, - DirListFlags::Flags flags, - ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFileSystem::DirList" ); - return pFileSystem->DirList( path, flags, handler, timeout ); - } - - //------------------------------------------------------------------------ - // SendInfo - //------------------------------------------------------------------------ - virtual XRootDStatus SendInfo( const std::string &info, - ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFileSystem::SendInfo" ); - return pFileSystem->SendInfo( info, handler, timeout ); - } - - //------------------------------------------------------------------------ - // Prepare - //------------------------------------------------------------------------ - virtual XRootDStatus Prepare( const std::vector &fileList, - PrepareFlags::Flags flags, - uint8_t priority, - ResponseHandler *handler, - uint16_t timeout ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFileSystem::Prepare" ); - return pFileSystem->Prepare( fileList, flags, priority, handler, - timeout ); - } - - //------------------------------------------------------------------------ - // SetProperty - //------------------------------------------------------------------------ - virtual bool SetProperty( const std::string &name, - const std::string &value ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFileSystem::SetProperty" ); - return pFileSystem->SetProperty( name, value ); - } - - //------------------------------------------------------------------------ - // GetProperty - //------------------------------------------------------------------------ - virtual bool GetProperty( const std::string &name, - std::string &value ) const - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Calling IdentityFilesystem::GetProperty" ); - return pFileSystem->GetProperty( name, value ); - } - - private: - XrdCl::FileSystem *pFileSystem; - }; -} - -namespace XrdClTests -{ - //---------------------------------------------------------------------------- - // Create a file plug-in for the given URL - //---------------------------------------------------------------------------- - FilePlugIn *IdentityFactory::CreateFile( const std::string &url ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Creating an identity file plug-in" ); - return new IdentityFile(); - } - - //---------------------------------------------------------------------------- - // Create a file system plug-in for the given URL - //---------------------------------------------------------------------------- - FileSystemPlugIn *IdentityFactory::CreateFileSystem( const std::string &url ) - { - XrdCl::Log *log = TestEnv::GetLog(); - log->Debug( 1, "Creating an identity file system plug-in" ); - return new IdentityFileSystem( url ); - } -} - diff --git a/tests/XrdClTests/IdentityPlugIn.hh b/tests/XrdClTests/IdentityPlugIn.hh deleted file mode 100644 index ebc7e45600d..00000000000 --- a/tests/XrdClTests/IdentityPlugIn.hh +++ /dev/null @@ -1,55 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2014 by European Organization for Nuclear Research (CERN) -// Author: Lukasz Janyst -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -// -// In applying this licence, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -//------------------------------------------------------------------------------ - -#ifndef __XRDCLTESTS_IDENTITY_PLUGIN_HH__ -#define __XRDCLTESTS_IDENTITY_PLUGIN_HH__ - -#include "XrdCl/XrdClPlugInInterface.hh" - -namespace XrdClTests -{ - //---------------------------------------------------------------------------- - // Plugin factory - //---------------------------------------------------------------------------- - class IdentityFactory: public XrdCl::PlugInFactory - { - public: - //------------------------------------------------------------------------ - // Destructor - //------------------------------------------------------------------------ - virtual ~IdentityFactory() {} - - //------------------------------------------------------------------------ - // Create a file plug-in for the given URL - //------------------------------------------------------------------------ - virtual XrdCl::FilePlugIn *CreateFile( const std::string &url ); - - //------------------------------------------------------------------------ - // Create a file system plug-in for the given URL - //------------------------------------------------------------------------ - virtual XrdCl::FileSystemPlugIn *CreateFileSystem( const std::string &url ); - }; -}; - -#endif // __XRDCLTESTS_IDENTITY_PLUGIN_HH__ diff --git a/tests/XrdClTests/LocalFileHandlerTest.cc b/tests/XrdClTests/LocalFileHandlerTest.cc deleted file mode 100644 index f8618c14f7b..00000000000 --- a/tests/XrdClTests/LocalFileHandlerTest.cc +++ /dev/null @@ -1,550 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH -// Author: Paul-Niklas Kramp -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -//------------------------------------------------------------------------------ -#include -#include "TestEnv.hh" -#include "CppUnitXrdHelpers.hh" -#include "XrdCl/XrdClFile.hh" - -#include -#include -#include - -using namespace XrdClTests; - -//------------------------------------------------------------------------------ -// Declaration -//------------------------------------------------------------------------------ -class LocalFileHandlerTest: public CppUnit::TestCase -{ - public: - CPPUNIT_TEST_SUITE( LocalFileHandlerTest ); - CPPUNIT_TEST( OpenCloseTest ); - CPPUNIT_TEST( ReadTest ); - CPPUNIT_TEST( ReadWithOffsetTest ); - CPPUNIT_TEST( WriteTest ); - CPPUNIT_TEST( WriteWithOffsetTest ); - CPPUNIT_TEST( WriteMkdirTest ); - CPPUNIT_TEST( TruncateTest ); - CPPUNIT_TEST( VectorReadTest ); - CPPUNIT_TEST( VectorWriteTest ); - CPPUNIT_TEST( SyncTest ); - CPPUNIT_TEST( WriteVTest ); - CPPUNIT_TEST( XAttrTest ); - CPPUNIT_TEST_SUITE_END(); - void CreateTestFileFunc( std::string url, std::string content = "GenericTestFile" ); - void OpenCloseTest(); - void ReadTest(); - void ReadWithOffsetTest(); - void WriteTest(); - void WriteWithOffsetTest(); - void WriteMkdirTest(); - void TruncateTest(); - void VectorReadTest(); - void VectorWriteTest(); - void SyncTest(); - void WriteVTest(); - void XAttrTest(); -}; -CPPUNIT_TEST_SUITE_REGISTRATION( LocalFileHandlerTest ); - -//---------------------------------------------------------------------------- -// Create the file to be tested -//---------------------------------------------------------------------------- -void LocalFileHandlerTest::CreateTestFileFunc( std::string url, std::string content ){ - mode_t openmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; - int fd = open( url.c_str(), O_RDWR | O_CREAT | O_TRUNC, openmode ); - int rc = write( fd, content.c_str(), content.size() ); - CPPUNIT_ASSERT_EQUAL( rc, int( content.size() ) ); - rc = close( fd ); - CPPUNIT_ASSERT_EQUAL( rc, 0 ); -} - -void LocalFileHandlerTest::SyncTest(){ - using namespace XrdCl; - std::string targetURL = "/tmp/lfilehandlertestfilesync"; - CreateTestFileFunc( targetURL ); - - //---------------------------------------------------------------------------- - // Open and Sync File - //---------------------------------------------------------------------------- - OpenFlags::Flags flags = OpenFlags::Update; - Access::Mode mode = Access::UR | Access::UW | Access::GR | Access::OR; - File *file = new File(); - CPPUNIT_ASSERT_XRDST( file->Open( targetURL, flags, mode ) ); - CPPUNIT_ASSERT_XRDST( file->Sync() ); - CPPUNIT_ASSERT_XRDST( file->Close() ); - CPPUNIT_ASSERT( remove( targetURL.c_str() ) == 0 ); - delete file; -} - -void LocalFileHandlerTest::OpenCloseTest(){ - using namespace XrdCl; - std::string targetURL = "/tmp/lfilehandlertestfileopenclose"; - CreateTestFileFunc( targetURL ); - - //---------------------------------------------------------------------------- - // Open existing file - //---------------------------------------------------------------------------- - OpenFlags::Flags flags = OpenFlags::Update; - Access::Mode mode = Access::UR|Access::UW|Access::GR|Access::OR; - File *file = new File(); - CPPUNIT_ASSERT_XRDST( file->Open( targetURL, flags, mode ) ); - CPPUNIT_ASSERT( file->IsOpen() ); - CPPUNIT_ASSERT_XRDST( file->Close() ); - CPPUNIT_ASSERT( remove( targetURL.c_str() ) == 0 ); - - //---------------------------------------------------------------------------- - // Try open non-existing file - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT( !file->Open( targetURL, flags, mode ).IsOK() ); - CPPUNIT_ASSERT( !file->IsOpen() ); - - //---------------------------------------------------------------------------- - // Try close non-opened file, return has to be error - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT( file->Close().status == stError ); - delete file; -} - -void LocalFileHandlerTest::WriteTest(){ - using namespace XrdCl; - std::string targetURL = "/tmp/lfilehandlertestfilewrite"; - std::string toBeWritten = "tenBytes1\0"; - uint32_t writeSize = toBeWritten.size(); - CreateTestFileFunc( targetURL, "" ); - char *buffer = new char[writeSize]; - //---------------------------------------------------------------------------- - // Open and Write File - //---------------------------------------------------------------------------- - OpenFlags::Flags flags = OpenFlags::Update; - Access::Mode mode = Access::UR|Access::UW|Access::GR|Access::OR; - File *file = new File(); - CPPUNIT_ASSERT_XRDST( file->Open( targetURL, flags, mode ) ); - CPPUNIT_ASSERT( file->IsOpen() ); - CPPUNIT_ASSERT_XRDST( file->Write( 0, writeSize, toBeWritten.c_str()) ); - CPPUNIT_ASSERT_XRDST( file->Close() ); - - //---------------------------------------------------------------------------- - // Read file with POSIX calls to confirm correct write - //---------------------------------------------------------------------------- - int fd = open( targetURL.c_str(), O_RDWR ); - int rc = read( fd, buffer, int( writeSize ) ); - CPPUNIT_ASSERT_EQUAL( rc, int( writeSize ) ); - std::string read( (char *)buffer, writeSize ); - CPPUNIT_ASSERT( toBeWritten == read ); - CPPUNIT_ASSERT( remove( targetURL.c_str() ) == 0 ); - delete[] buffer; - delete file; -} - -void LocalFileHandlerTest::WriteWithOffsetTest(){ - using namespace XrdCl; - std::string targetURL = "/tmp/lfilehandlertestfilewriteoffset"; - std::string toBeWritten = "tenBytes10"; - std::string notToBeOverwritten = "front"; - uint32_t writeSize = toBeWritten.size(); - uint32_t offset = notToBeOverwritten.size(); - void *buffer = new char[offset]; - CreateTestFileFunc( targetURL, notToBeOverwritten ); - - //---------------------------------------------------------------------------- - // Open and Write File - //---------------------------------------------------------------------------- - OpenFlags::Flags flags = OpenFlags::Update; - Access::Mode mode = Access::UR|Access::UW|Access::GR|Access::OR; - File *file = new File(); - CPPUNIT_ASSERT_XRDST( file->Open( targetURL, flags, mode ) ); - CPPUNIT_ASSERT( file->IsOpen() ); - CPPUNIT_ASSERT_XRDST( file->Write( offset, writeSize, toBeWritten.c_str()) ); - CPPUNIT_ASSERT_XRDST( file->Close() ); - - //---------------------------------------------------------------------------- - // Read file with POSIX calls to confirm correct write - //---------------------------------------------------------------------------- - int fd = open( targetURL.c_str(), O_RDWR ); - int rc = read( fd, buffer, offset ); - CPPUNIT_ASSERT_EQUAL( rc, int( offset ) ); - std::string read( (char *)buffer, offset ); - CPPUNIT_ASSERT( notToBeOverwritten == read ); - CPPUNIT_ASSERT( remove( targetURL.c_str() ) == 0 ); - delete[] (char*)buffer; - delete file; -} - -void LocalFileHandlerTest::WriteMkdirTest(){ - using namespace XrdCl; - std::string targetURL = "/tmp/testdir/further/muchfurther/evenfurther/lfilehandlertestfilewrite"; - std::string toBeWritten = "tenBytes10"; - uint32_t writeSize = toBeWritten.size(); - char *buffer = new char[writeSize]; - - //---------------------------------------------------------------------------- - // Open and Write File - //---------------------------------------------------------------------------- - OpenFlags::Flags flags = OpenFlags::Update | OpenFlags::MakePath | OpenFlags::New; - Access::Mode mode = Access::UR|Access::UW|Access::UX; - File *file = new File(); - CPPUNIT_ASSERT_XRDST( file->Open( targetURL, flags, mode ) ); - CPPUNIT_ASSERT( file->IsOpen() ); - CPPUNIT_ASSERT_XRDST( file->Write( 0, writeSize, toBeWritten.c_str()) ); - CPPUNIT_ASSERT_XRDST( file->Close() ); - - //---------------------------------------------------------------------------- - // Read file with POSIX calls to confirm correct write - //---------------------------------------------------------------------------- - int fd = open( targetURL.c_str(), O_RDWR ); - int rc = read( fd, buffer, writeSize ); - CPPUNIT_ASSERT_EQUAL( rc, int( writeSize ) ); - std::string read( buffer, writeSize ); - CPPUNIT_ASSERT( toBeWritten == read ); - CPPUNIT_ASSERT( remove( targetURL.c_str() ) == 0 ); - delete[] buffer; - delete file; -} - -void LocalFileHandlerTest::ReadTest(){ - using namespace XrdCl; - std::string targetURL = "/tmp/lfilehandlertestfileread"; - std::string toBeWritten = "tenBytes10"; - uint32_t offset = 0; - uint32_t writeSize = toBeWritten.size(); - char *buffer = new char[writeSize]; - uint32_t bytesRead = 0; - - //---------------------------------------------------------------------------- - // Write file with POSIX calls to ensure correct write - //---------------------------------------------------------------------------- - CreateTestFileFunc( targetURL, toBeWritten ); - - //---------------------------------------------------------------------------- - // Open and Read File - //---------------------------------------------------------------------------- - OpenFlags::Flags flags = OpenFlags::Update; - Access::Mode mode = Access::UR|Access::UW|Access::GR|Access::OR; - File *file = new File(); - CPPUNIT_ASSERT_XRDST( file->Open( targetURL, flags, mode ) ); - CPPUNIT_ASSERT( file->IsOpen() ); - CPPUNIT_ASSERT_XRDST( file->Read( offset, writeSize, buffer, bytesRead ) ); - CPPUNIT_ASSERT_XRDST( file->Close() ); - - std::string read( (char*)buffer, writeSize ); - CPPUNIT_ASSERT( toBeWritten == read ); - CPPUNIT_ASSERT( remove( targetURL.c_str() ) == 0 ); - - delete[] buffer; - delete file; -} - -void LocalFileHandlerTest::ReadWithOffsetTest(){ - using namespace XrdCl; - std::string targetURL = "/tmp/lfilehandlertestfileread"; - std::string toBeWritten = "tenBytes10"; - uint32_t offset = 3; - std::string expectedRead = "Byte"; - uint32_t readsize = expectedRead.size(); - char *buffer = new char[readsize]; - uint32_t bytesRead = 0; - - //---------------------------------------------------------------------------- - // Write file with POSIX calls to ensure correct write - //---------------------------------------------------------------------------- - CreateTestFileFunc( targetURL, toBeWritten ); - - //---------------------------------------------------------------------------- - // Open and Read File - //---------------------------------------------------------------------------- - OpenFlags::Flags flags = OpenFlags::Update; - Access::Mode mode = Access::UR|Access::UW|Access::GR|Access::OR; - File *file = new File(); - CPPUNIT_ASSERT_XRDST( file->Open( targetURL, flags, mode ) ); - CPPUNIT_ASSERT( file->IsOpen() ); - CPPUNIT_ASSERT_XRDST( file->Read( offset, readsize, buffer, bytesRead ) ); - CPPUNIT_ASSERT_XRDST( file->Close() ); - - std::string read( buffer, readsize ); - CPPUNIT_ASSERT( expectedRead == read ); - CPPUNIT_ASSERT( remove( targetURL.c_str() ) == 0 ); - delete[] buffer; - delete file; -} - -void LocalFileHandlerTest::TruncateTest(){ - using namespace XrdCl; - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - std::string targetURL = "/tmp/lfilehandlertestfiletruncate"; - - CreateTestFileFunc(targetURL); - //---------------------------------------------------------------------------- - // Prepare truncate - //---------------------------------------------------------------------------- - OpenFlags::Flags flags = OpenFlags::Update | OpenFlags::Force; - Access::Mode mode = Access::UR|Access::UW|Access::GR|Access::OR; - File *file = new File(); - uint32_t bytesRead = 0; - uint32_t truncateSize = 5; - - //---------------------------------------------------------------------------- - // Read after truncate, but with greater length. bytesRead must still be - // truncate size if truncate works as intended - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( file->Open( targetURL, flags, mode ) ); - CPPUNIT_ASSERT_XRDST( file->Truncate( truncateSize ) ); - char *buffer = new char[truncateSize + 3]; - CPPUNIT_ASSERT_XRDST( file->Read( 0, truncateSize + 3, buffer, bytesRead ) ); - CPPUNIT_ASSERT_EQUAL( truncateSize, bytesRead ); - CPPUNIT_ASSERT_XRDST( file->Close() ); - CPPUNIT_ASSERT( remove( targetURL.c_str() ) == 0 ); - delete file; - delete[] buffer; -} - -void LocalFileHandlerTest::VectorReadTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - std::string targetURL = "/tmp/lfilehandlertestfilevectorread"; - CreateTestFileFunc( targetURL ); - VectorReadInfo *info = 0; - ChunkList chunks; - - //---------------------------------------------------------------------------- - // Prepare VectorRead - //---------------------------------------------------------------------------- - OpenFlags::Flags flags = OpenFlags::Update; - Access::Mode mode = Access::UR|Access::UW|Access::GR|Access::OR; - File file; - CPPUNIT_ASSERT_XRDST( file.Open( targetURL, flags, mode ) ); - - //---------------------------------------------------------------------------- - // VectorRead no cursor - //---------------------------------------------------------------------------- - - chunks.push_back( ChunkInfo( 0, 5, new char[5] ) ); - chunks.push_back( ChunkInfo( 10, 5, new char[5] ) ); - CPPUNIT_ASSERT_XRDST( file.VectorRead( chunks, NULL, info ) ); - CPPUNIT_ASSERT_XRDST( file.Close() ); - CPPUNIT_ASSERT( info->GetSize() == 10 ); - CPPUNIT_ASSERT_EQUAL( 0, memcmp( "Gener", - info->GetChunks()[0].buffer, - info->GetChunks()[0].length ) ); - CPPUNIT_ASSERT_EQUAL( 0, memcmp( "tFile", - info->GetChunks()[1].buffer, - info->GetChunks()[1].length ) ); - delete[] (char*)chunks[0].buffer; - delete[] (char*)chunks[1].buffer; - delete info; - - //---------------------------------------------------------------------------- - // VectorRead cursor - //---------------------------------------------------------------------------- - char *buffer = new char[10]; - chunks.clear(); - chunks.push_back( ChunkInfo( 0, 5, 0 ) ); - chunks.push_back( ChunkInfo( 10, 5, 0 ) ); - info = 0; - - CPPUNIT_ASSERT_XRDST( file.Open( targetURL, flags, mode ) ); - CPPUNIT_ASSERT_XRDST( file.VectorRead( chunks, buffer, info ) ); - CPPUNIT_ASSERT_XRDST( file.Close() ); - CPPUNIT_ASSERT( info->GetSize() == 10 ); - CPPUNIT_ASSERT_EQUAL( 0, memcmp( "GenertFile", - info->GetChunks()[0].buffer, - info->GetChunks()[0].length ) ); - CPPUNIT_ASSERT( remove( targetURL.c_str() ) == 0 ); - - delete[] buffer; - delete info; -} - -void LocalFileHandlerTest::VectorWriteTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - std::string targetURL = "/tmp/lfilehandlertestfilevectorwrite"; - CreateTestFileFunc( targetURL ); - ChunkList chunks; - - //---------------------------------------------------------------------------- - // Prepare VectorWrite - //---------------------------------------------------------------------------- - OpenFlags::Flags flags = OpenFlags::Update; - Access::Mode mode = Access::UR|Access::UW|Access::GR|Access::OR; - File file; - CPPUNIT_ASSERT_XRDST( file.Open( targetURL, flags, mode ) ); - - //---------------------------------------------------------------------------- - // VectorWrite - //---------------------------------------------------------------------------- - - ChunkInfo chunk( 0, 5, new char[5] ); - memset( chunk.buffer, 'A', chunk.length ); - chunks.push_back( chunk ); - chunk = ChunkInfo( 10, 5, new char[5] ); - memset( chunk.buffer, 'B', chunk.length ); - chunks.push_back( chunk ); - - CPPUNIT_ASSERT_XRDST( file.VectorWrite( chunks ) ); - - //---------------------------------------------------------------------------- - // Verify with VectorRead - //---------------------------------------------------------------------------- - - VectorReadInfo *info = 0; - char *buffer = new char[10]; - CPPUNIT_ASSERT_XRDST( file.VectorRead( chunks, buffer, info ) ); - - CPPUNIT_ASSERT_EQUAL( 0, memcmp( buffer, "AAAAABBBBB", 10 ) ); - - CPPUNIT_ASSERT_XRDST( file.Close() ); - CPPUNIT_ASSERT( info->GetSize() == 10 ); - - delete[] (char*)chunks[0].buffer; - delete[] (char*)chunks[1].buffer; - delete[] buffer; - delete info; - - CPPUNIT_ASSERT( remove( targetURL.c_str() ) == 0 ); -} - -void LocalFileHandlerTest::WriteVTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - std::string targetURL = "/tmp/lfilehandlertestfilewritev"; - CreateTestFileFunc( targetURL ); - - //---------------------------------------------------------------------------- - // Prepare WriteV - //---------------------------------------------------------------------------- - OpenFlags::Flags flags = OpenFlags::Update; - Access::Mode mode = Access::UR|Access::UW|Access::GR|Access::OR; - File file; - CPPUNIT_ASSERT_XRDST( file.Open( targetURL, flags, mode ) ); - - char str[] = "WriteVTest"; - std::vector buffer( 10 ); - std::copy( str, str + sizeof( str ) - 1, buffer.begin() ); - int iovcnt = 2; - iovec iov[iovcnt]; - iov[0].iov_base = buffer.data(); - iov[0].iov_len = 6; - iov[1].iov_base = buffer.data() + 6; - iov[1].iov_len = 4; - CPPUNIT_ASSERT_XRDST( file.WriteV( 7, iov, iovcnt ) ); - - uint32_t bytesRead = 0; - buffer.resize( 17 ); - CPPUNIT_ASSERT_XRDST( file.Read( 0, 17, buffer.data(), bytesRead ) ); - CPPUNIT_ASSERT( buffer.size() == 17 ); - std::string expected = "GenericWriteVTest"; - CPPUNIT_ASSERT( std::string( buffer.data(), buffer.size() ) == expected ); - CPPUNIT_ASSERT_XRDST( file.Close() ); - CPPUNIT_ASSERT( remove( targetURL.c_str() ) == 0 ); -} - -void LocalFileHandlerTest::XAttrTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - // (we do the test in /data as /tmp might be on tpmfs, - // which does not support xattrs) - //---------------------------------------------------------------------------- - std::string targetURL = "/data/lfilehandlertestfilexattr"; - CreateTestFileFunc( targetURL ); - - File f; - CPPUNIT_ASSERT_XRDST( f.Open( targetURL, OpenFlags::Update ) ); - - //---------------------------------------------------------------------------- - // Test XAttr Set - //---------------------------------------------------------------------------- - std::vector attrs; - attrs.push_back( xattr_t( "version", "v3.3.3" ) ); - attrs.push_back( xattr_t( "description", "a very important file" ) ); - attrs.push_back( xattr_t( "checksum", "0x22334455" ) ); - - std::vector st_resp; - - CPPUNIT_ASSERT_XRDST( f.SetXAttr( attrs, st_resp ) ); - - std::vector::iterator itr1; - for( itr1 = st_resp.begin(); itr1 != st_resp.end(); ++itr1 ) - CPPUNIT_ASSERT_XRDST( itr1->status ); - - //---------------------------------------------------------------------------- - // Test XAttr Get - //---------------------------------------------------------------------------- - std::vector names; - names.push_back( "version" ); - names.push_back( "description" ); - std::vector resp; - CPPUNIT_ASSERT_XRDST( f.GetXAttr( names, resp ) ); - - CPPUNIT_ASSERT_XRDST( resp[0].status ); - CPPUNIT_ASSERT_XRDST( resp[1].status ); - - CPPUNIT_ASSERT( resp.size() == 2 ); - int vid = resp[0].name == "version" ? 0 : 1; - int did = vid == 0 ? 1 : 0; - CPPUNIT_ASSERT( resp[vid].name == "version" && - resp[vid].value == "v3.3.3" ); - CPPUNIT_ASSERT( resp[did].name == "description" && - resp[did].value == "a very important file" ); - - //---------------------------------------------------------------------------- - // Test XAttr Del - //---------------------------------------------------------------------------- - names.clear(); - names.push_back( "description" ); - st_resp.clear(); - CPPUNIT_ASSERT_XRDST( f.DelXAttr( names, st_resp ) ); - CPPUNIT_ASSERT( st_resp.size() == 1 ); - CPPUNIT_ASSERT_XRDST( st_resp[0].status ); - - //---------------------------------------------------------------------------- - // Test XAttr List - //---------------------------------------------------------------------------- - resp.clear(); - CPPUNIT_ASSERT_XRDST( f.ListXAttr( resp ) ); - CPPUNIT_ASSERT( resp.size() == 2 ); - vid = resp[0].name == "version" ? 0 : 1; - int cid = vid == 0 ? 1 : 0; - CPPUNIT_ASSERT( resp[vid].name == "version" && - resp[vid].value == "v3.3.3" ); - CPPUNIT_ASSERT( resp[cid].name == "checksum" && - resp[cid].value == "0x22334455" ); - - //---------------------------------------------------------------------------- - // Cleanup - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( f.Close() ); - CPPUNIT_ASSERT( remove( targetURL.c_str() ) == 0 ); -} diff --git a/tests/XrdClTests/MonitorTestLib.cc b/tests/XrdClTests/MonitorTestLib.cc deleted file mode 100644 index d5451084213..00000000000 --- a/tests/XrdClTests/MonitorTestLib.cc +++ /dev/null @@ -1,213 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 by European Organization for Nuclear Research (CERN) -// Author: Lukasz Janyst -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -//------------------------------------------------------------------------------ - -#include "XrdCl/XrdClMonitor.hh" -#include "XrdCl/XrdClLog.hh" -#include "XrdCl/XrdClUtils.hh" -#include "XrdVersion.hh" - -#include "TestEnv.hh" - -XrdVERSIONINFO( XrdClGetMonitor, MonitorTest ); - -class MonitorTest: public XrdCl::Monitor -{ - public: - //-------------------------------------------------------------------------- - // Contructor - //-------------------------------------------------------------------------- - MonitorTest( const std::string &exec, const std::string ¶m ): - pExec( exec ), - pParam( param ), - pInitialized(false) - { - XrdCl::Log *log = XrdClTests::TestEnv::GetLog(); - log->Debug( 2, "Constructed monitoring, exec %s, param %s", - exec.c_str(), param.c_str() ); - } - - //-------------------------------------------------------------------------- - // Destructor - //-------------------------------------------------------------------------- - virtual ~MonitorTest() {} - - //-------------------------------------------------------------------------- - // Event - //-------------------------------------------------------------------------- - virtual void Event( EventCode evCode, void *evData ) - { - using namespace XrdCl; - using namespace XrdClTests; - - Log *log = TestEnv::GetLog(); - switch( evCode ) - { - //---------------------------------------------------------------------- - // Got a connect event - //---------------------------------------------------------------------- - case EvConnect: - { - ConnectInfo *i = (ConnectInfo*)evData; - std::string timeStarted = Utils::TimeToString( i->sTOD.tv_sec ); - std::string timeDone = Utils::TimeToString( i->sTOD.tv_sec ); - log->Debug( 2, "Successfully connected to: %s, started: %s, " - "finished: %s, authentication: %s, streams: %d", - i->server.c_str(), timeStarted.c_str(), timeDone.c_str(), - i->auth.empty() ? "none" : i->auth.c_str(), - i->streams ); - break; - } - - //---------------------------------------------------------------------- - // Got a disconnect event - //---------------------------------------------------------------------- - case EvDisconnect: - { - DisconnectInfo *i = (DisconnectInfo*)evData; - log->Debug( 2, "Disconnected from: %s, bytes sent: %ld, " - "bytes received: %ld, connection time: %d, " - "disconnection status: %s", - i->server.c_str(), i->sBytes, i->rBytes, - i->cTime, i->status.ToString().c_str() ); - break; - } - - //---------------------------------------------------------------------- - // Got an open event - //---------------------------------------------------------------------- - case EvOpen: - { - OpenInfo *i = (OpenInfo*)evData; - log->Debug( 2, "Successfully opened file %s at %s, size %ld", - i->file->GetURL().c_str(), i->dataServer.c_str(), - i->fSize ); - break; - } - - //---------------------------------------------------------------------- - // Got a close event - //---------------------------------------------------------------------- - case EvClose: - { - CloseInfo *i = (CloseInfo*)evData; - std::string timeOpen = Utils::TimeToString( i->oTOD.tv_sec ); - std::string timeClosed = Utils::TimeToString( i->cTOD.tv_sec ); - log->Debug( 2, "Closed file %s, opened: %s, closed: %s, status: %s", - i->file->GetURL().c_str(), timeOpen.c_str(), - timeClosed.c_str(), i->status->ToStr().c_str() ); - log->Debug( 2, "Closed file %s, bytes: read: %ld, readv: %ld, write:" - " %ld, writev: %ld", i->file->GetURL().c_str(), i->rBytes, i->vrBytes, - i->wBytes, i->vwBytes ); - log->Debug( 2, "Closed file %s, count: read: %d, readv: %d/%d, " - "write: %d", i->file->GetURL().c_str(), i->rCount, - i->vCount, i->vSegs, i->wCount ); - - break; - } - - //---------------------------------------------------------------------- - // Got an error event - //---------------------------------------------------------------------- - case EvErrIO: - { - ErrorInfo *i = (ErrorInfo*)evData; - std::string op; - switch( i->opCode ) - { - case ErrorInfo::ErrOpen: op = "Open"; break; - case ErrorInfo::ErrRead: op = "Read"; break; - case ErrorInfo::ErrReadV: op = "ReadV"; break; - case ErrorInfo::ErrWrite: op = "Write"; break; - case ErrorInfo::ErrWriteV: op = "WriteV"; break; - case ErrorInfo::ErrUnc: op = "Unclassified"; break; - }; - log->Debug( 2, "Operation on file %s encountered an error: %s " - "while %s", i->file->GetURL().c_str(), - i->status->ToStr().c_str(), op.c_str() ); - break; - } - - //---------------------------------------------------------------------- - // Got a copy begin event - //---------------------------------------------------------------------- - case EvCopyBeg: - { - CopyBInfo *i = (CopyBInfo*)evData; - log->Debug( 2, "Copy operation started: origin %s, target: %s ", - i->transfer.origin->GetURL().c_str(), - i->transfer.target->GetURL().c_str() ); - break; - } - - //---------------------------------------------------------------------- - // Got a copy end event - //---------------------------------------------------------------------- - case EvCopyEnd: - { - CopyEInfo *i = (CopyEInfo*)evData; - std::string timeStart = Utils::TimeToString( i->bTOD.tv_sec ); - std::string timeEnd = Utils::TimeToString( i->eTOD.tv_sec ); - log->Debug( 2, "Copy operation ended: origin: %s, target: %s, " - "start time %s, end time: %s, status: %s", - i->transfer.origin->GetURL().c_str(), - i->transfer.target->GetURL().c_str(), - timeStart.c_str(), timeEnd.c_str(), - i->status->ToStr().c_str() ); - break; - } - - //---------------------------------------------------------------------- - // Got a copy end event - //---------------------------------------------------------------------- - case EvCheckSum: - { - CheckSumInfo *i = (CheckSumInfo*)evData; - log->Debug( 2, "Checksum for transfer: origin: %s, target: %s, " - "checksum %s, is ok: %d", - i->transfer.origin->GetURL().c_str(), - i->transfer.target->GetURL().c_str(), - i->cksum.c_str(), (int)i->isOK ); - log->Debug( 2, "Checksum for transfer: origin: %s, target: %s, " - "us elapsed at origin %ld, us leapsed at target: %ld", - i->transfer.origin->GetURL().c_str(), - i->transfer.target->GetURL().c_str(), - i->oTime, i->tTime ); - break; - } - } - } - - private: - std::string pExec; - std::string pParam; - bool pInitialized; -}; - -//------------------------------------------------------------------------------ -// C-mangled symbol for dlopen -//------------------------------------------------------------------------------ -extern "C" -{ - void *XrdClGetMonitor( const char *exec, const char *param ) - { - XrdCl::Log *log = XrdClTests::TestEnv::GetLog(); - log->Debug( 2, "Constructing monitoring, exec %s, param %s", - exec, param ? param : "" ); - return new MonitorTest( exec, param ? param : "" ); - } -} diff --git a/tests/XrdClTests/OperationsWorkflowTest.cc b/tests/XrdClTests/OperationsWorkflowTest.cc deleted file mode 100644 index 6f919d67af1..00000000000 --- a/tests/XrdClTests/OperationsWorkflowTest.cc +++ /dev/null @@ -1,991 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2011-2017 by European Organization for Nuclear Research (CERN) -// Author: Krzysztof Jamrog -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -// -// In applying this licence, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -//------------------------------------------------------------------------------ - -#include -#include "TestEnv.hh" -#include "IdentityPlugIn.hh" -#include "CppUnitXrdHelpers.hh" -#include "XrdCl/XrdClURL.hh" -#include "XrdCl/XrdClOperations.hh" -#include "XrdCl/XrdClParallelOperation.hh" -#include "XrdCl/XrdClFileOperations.hh" -#include "XrdCl/XrdClFileSystemOperations.hh" -#include "XrdCl/XrdClCheckpointOperation.hh" -#include "XrdCl/XrdClFwd.hh" - -#include - -using namespace XrdClTests; - -//------------------------------------------------------------------------------ -// Declaration -//------------------------------------------------------------------------------ -class WorkflowTest: public CppUnit::TestCase -{ - public: - CPPUNIT_TEST_SUITE( WorkflowTest ); - CPPUNIT_TEST( ReadingWorkflowTest ); - CPPUNIT_TEST( WritingWorkflowTest ); - CPPUNIT_TEST( MissingParameterTest ); - CPPUNIT_TEST( OperationFailureTest ); - CPPUNIT_TEST( DoubleRunningTest ); - CPPUNIT_TEST( ParallelTest ); - CPPUNIT_TEST( FileSystemWorkflowTest ); - CPPUNIT_TEST( MixedWorkflowTest ); - CPPUNIT_TEST( WorkflowWithFutureTest ); - CPPUNIT_TEST( XAttrWorkflowTest ); - CPPUNIT_TEST( MkDirAsyncTest ); - CPPUNIT_TEST( CheckpointTest ); - CPPUNIT_TEST_SUITE_END(); - void ReadingWorkflowTest(); - void WritingWorkflowTest(); - void MissingParameterTest(); - void OperationFailureTest(); - void DoubleRunningTest(); - void ParallelTest(); - void FileSystemWorkflowTest(); - void MixedWorkflowTest(); - void WorkflowWithFutureTest(); - void XAttrWorkflowTest(); - void MkDirAsyncTest(); - void CheckpointTest(); -}; - -CPPUNIT_TEST_SUITE_REGISTRATION( WorkflowTest ); - - -namespace { - using namespace XrdCl; - - XrdCl::URL GetAddress(){ - Env *testEnv = TestEnv::GetEnv(); - std::string address; - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - return XrdCl::URL(address); - } - - std::string GetPath(const std::string &fileName){ - Env *testEnv = TestEnv::GetEnv(); - - std::string dataPath; - CPPUNIT_ASSERT( testEnv->GetString( "DataPath", dataPath ) ); - - return dataPath + "/" + fileName; - } - - - std::string GetFileUrl(const std::string &fileName){ - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "DataPath", dataPath ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - std::string path = dataPath + "/" + fileName; - std::string fileUrl = address + "/" + path; - - return fileUrl; - } - - class TestingHandler: public ResponseHandler { - public: - TestingHandler(){ - executed = false; - } - - void HandleResponseWithHosts(XrdCl::XRootDStatus *status, XrdCl::AnyObject *response, XrdCl::HostList *hostList) { - delete hostList; - HandleResponse(status, response); - } - - void HandleResponse(XrdCl::XRootDStatus *status, XrdCl::AnyObject *response) { - CPPUNIT_ASSERT_XRDST(*status); - delete status; - delete response; - executed = true; - } - - bool Executed(){ - return executed; - } - - protected: - bool executed; - }; - - class ExpectErrorHandler: public ResponseHandler - { - public: - ExpectErrorHandler(){ - executed = false; - } - - void HandleResponseWithHosts(XrdCl::XRootDStatus *status, XrdCl::AnyObject *response, XrdCl::HostList *hostList) { - delete hostList; - HandleResponse(status, response); - } - - void HandleResponse(XrdCl::XRootDStatus *status, XrdCl::AnyObject *response) { - CPPUNIT_ASSERT( !status->IsOK() ); - delete status; - delete response; - executed = true; - } - - bool Executed(){ - return executed; - } - - protected: - bool executed; - }; - - - -} - - -void WorkflowTest::ReadingWorkflowTest(){ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - std::string fileUrl = GetFileUrl("cb4aacf1-6f28-42f2-b68a-90a73460f424.dat"); - File f; - - //---------------------------------------------------------------------------- - // Create handlers - //---------------------------------------------------------------------------- - TestingHandler openHandler; - TestingHandler readHandler; - TestingHandler closeHandler; - - //---------------------------------------------------------------------------- - // Forward parameters between operations - //---------------------------------------------------------------------------- - Fwd size; - Fwd buffer; - - //---------------------------------------------------------------------------- - // Create and execute workflow - //---------------------------------------------------------------------------- - - const OpenFlags::Flags flags = OpenFlags::Read; - uint64_t offset = 0; - - auto &&pipe = Open( f, fileUrl, flags ) >> openHandler // by reference - | Stat( f, true) >> [size, buffer]( XRootDStatus &status, StatInfo &stat ) mutable - { - CPPUNIT_ASSERT_XRDST( status ); - CPPUNIT_ASSERT( stat.GetSize() == 1048576000 ); - size = stat.GetSize(); - buffer = new char[stat.GetSize()]; - } - | Read( f, offset, size, buffer ) >> &readHandler // by pointer - | Close( f ) >> closeHandler; // by reference - - XRootDStatus status = WaitFor( pipe ); - CPPUNIT_ASSERT_XRDST( status ); - - CPPUNIT_ASSERT( openHandler.Executed() ); - CPPUNIT_ASSERT( readHandler.Executed() ); - CPPUNIT_ASSERT( closeHandler.Executed() ); - - delete[] reinterpret_cast( *buffer ); -} - - -void WorkflowTest::WritingWorkflowTest(){ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - std::string fileUrl = GetFileUrl("testFile.dat"); - auto flags = OpenFlags::Write | OpenFlags::Delete | OpenFlags::Update; - std::string texts[3] = {"First line\n", "Second line\n", "Third line\n"}; - File f; - - auto url = GetAddress(); - FileSystem fs(url); - auto relativePath = GetPath("testFile.dat"); - - auto createdFileSize = texts[0].size() + texts[1].size() + texts[2].size(); - - //---------------------------------------------------------------------------- - // Create handlers - //---------------------------------------------------------------------------- - std::packaged_task parser { - []( XRootDStatus& status, ChunkInfo &chunk ) - { - CPPUNIT_ASSERT_XRDST( status ); - char* buffer = reinterpret_cast( chunk.buffer ); - std::string ret( buffer, chunk.length ); - delete[] buffer; - return ret; - } - }; - std::future rdresp = parser.get_future(); - - //---------------------------------------------------------------------------- - // Forward parameters between operations - //---------------------------------------------------------------------------- - Fwd> iov; - Fwd size; - Fwd buffer; - - //---------------------------------------------------------------------------- - // Create and execute workflow - //---------------------------------------------------------------------------- - Pipeline pipe = Open( f, fileUrl, flags ) >> [iov, texts]( XRootDStatus &status ) mutable - { - CPPUNIT_ASSERT_XRDST( status ); - std::vector vec( 3 ); - vec[0].iov_base = strdup( texts[0].c_str() ); - vec[0].iov_len = texts[0].size(); - vec[1].iov_base = strdup( texts[1].c_str() ); - vec[1].iov_len = texts[1].size(); - vec[2].iov_base = strdup( texts[2].c_str() ); - vec[2].iov_len = texts[2].size(); - iov = std::move( vec ); - } - | WriteV( f, 0, iov ) - | Sync( f ) - | Stat( f, true ) >> [size, buffer, createdFileSize]( XRootDStatus &status, StatInfo &info ) mutable - { - CPPUNIT_ASSERT_XRDST( status ); - CPPUNIT_ASSERT( createdFileSize == info.GetSize() ); - size = info.GetSize(); - buffer = new char[info.GetSize()]; - } - | Read( f, 0, size, buffer ) >> parser - | Close( f ) - | Rm( fs, relativePath ); - - XRootDStatus status = WaitFor( std::move( pipe ) ); - CPPUNIT_ASSERT_XRDST( status ); - CPPUNIT_ASSERT( rdresp.get() == texts[0] + texts[1] + texts[2] ); - - free( (*iov)[0].iov_base ); - free( (*iov)[1].iov_base ); - free( (*iov)[2].iov_base ); -} - - -void WorkflowTest::MissingParameterTest(){ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - std::string fileUrl = GetFileUrl("cb4aacf1-6f28-42f2-b68a-90a73460f424.dat"); - File f; - - //---------------------------------------------------------------------------- - // Create handlers - //---------------------------------------------------------------------------- - ExpectErrorHandler readHandler; - - //---------------------------------------------------------------------------- - // Bad forwards - //---------------------------------------------------------------------------- - Fwd size; - Fwd buffer; - - //---------------------------------------------------------------------------- - // Create and execute workflow - //---------------------------------------------------------------------------- - - bool error = false, closed = false; - const OpenFlags::Flags flags = OpenFlags::Read; - uint64_t offset = 0; - - Pipeline pipe = Open( f, fileUrl, flags ) - | Stat( f, true ) - | Read( f, offset, size, buffer ) >> readHandler // by reference - | Close( f ) >> [&]( XRootDStatus& st ) - { - closed = true; - } - | Final( [&]( const XRootDStatus& st ) - { - error = !st.IsOK(); - }); - - XRootDStatus status = WaitFor( std::move( pipe ) ); - CPPUNIT_ASSERT( status.IsError() ); - //---------------------------------------------------------------------------- - // If there is an error, last handlers should not be executed - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT( readHandler.Executed() ); - CPPUNIT_ASSERT( !closed & error ); -} - - - -void WorkflowTest::OperationFailureTest(){ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - std::string fileUrl = GetFileUrl("noexisting.dat"); - File f; - - //---------------------------------------------------------------------------- - // Create handlers - //---------------------------------------------------------------------------- - ExpectErrorHandler openHandler; - std::future statresp; - std::future readresp; - std::future closeresp; - - //---------------------------------------------------------------------------- - // Create and execute workflow - //---------------------------------------------------------------------------- - - const OpenFlags::Flags flags = OpenFlags::Read; - auto &&pipe = Open( f, fileUrl, flags ) >> &openHandler // by pointer - | Stat( f, true ) >> statresp - | Read( f, 0, 0, nullptr ) >> readresp - | Close( f ) >> closeresp; - - XRootDStatus status = WaitFor( pipe ); // by obscure operation type - CPPUNIT_ASSERT( status.IsError() ); - - //---------------------------------------------------------------------------- - // If there is an error, handlers should not be executed - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT(openHandler.Executed()); - - try - { - statresp.get(); - } - catch( PipelineException &ex ) - { - CPPUNIT_ASSERT_XRDST_NOTOK( ex.GetError(), errPipelineFailed ); - } - - try - { - readresp.get(); - } - catch( PipelineException &ex ) - { - CPPUNIT_ASSERT_XRDST_NOTOK( ex.GetError(), errPipelineFailed ); - } - - try - { - closeresp.get(); - } - catch( PipelineException &ex ) - { - CPPUNIT_ASSERT_XRDST_NOTOK( ex.GetError(), errPipelineFailed ); - } -} - - -void WorkflowTest::DoubleRunningTest(){ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - std::string fileUrl = GetFileUrl("cb4aacf1-6f28-42f2-b68a-90a73460f424.dat"); - File f; - - //---------------------------------------------------------------------------- - // Create and execute workflow - //---------------------------------------------------------------------------- - - const OpenFlags::Flags flags = OpenFlags::Read; - bool opened = false, closed = false; - - auto &&pipe = Open( f, fileUrl, flags ) >> [&]( XRootDStatus &status ){ opened = status.IsOK(); } - | Close( f ) >> [&]( XRootDStatus &status ){ closed = status.IsOK(); }; - - std::future ftr = Async( pipe ); - - //---------------------------------------------------------------------------- - // Running workflow again should fail - //---------------------------------------------------------------------------- - try - { - Async( pipe ); - CPPUNIT_ASSERT( false ); - } - catch( std::logic_error &err ) - { - - } - - - XRootDStatus status = ftr.get(); - - //---------------------------------------------------------------------------- - // Running workflow again should fail - //---------------------------------------------------------------------------- - try - { - Async( pipe ); - CPPUNIT_ASSERT( false ); - } - catch( std::logic_error &err ) - { - - } - - CPPUNIT_ASSERT( status.IsOK() ); - - CPPUNIT_ASSERT( opened ); - CPPUNIT_ASSERT( closed ); -} - - -void WorkflowTest::ParallelTest(){ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - File lockFile; - File firstFile; - File secondFile; - - std::string lockFileName = "lockfile.lock"; - std::string dataFileName = "testFile.dat"; - - std::string lockUrl = GetFileUrl(lockFileName); - std::string firstFileUrl = GetFileUrl("cb4aacf1-6f28-42f2-b68a-90a73460f424.dat"); - std::string secondFileUrl = GetFileUrl(dataFileName); - - const auto readFlags = OpenFlags::Read; - const auto createFlags = OpenFlags::Delete; - - // ---------------------------------------------------------------------------- - // Create lock file and new data file - // ---------------------------------------------------------------------------- - auto f = new File(); - auto dataF = new File(); - - std::vector pipes; pipes.reserve( 2 ); - pipes.emplace_back( Open( f, lockUrl, createFlags ) | Close( f ) ); - pipes.emplace_back( Open( dataF, secondFileUrl, createFlags ) | Close( dataF ) ); - CPPUNIT_ASSERT_XRDST( WaitFor( Parallel( pipes ) >> []( XRootDStatus &status ){ CPPUNIT_ASSERT_XRDST( status ); } ) ); - CPPUNIT_ASSERT( pipes.empty() ); - - delete f; - delete dataF; - - //---------------------------------------------------------------------------- - // Create and execute workflow - //---------------------------------------------------------------------------- - uint64_t offset = 0; - uint32_t size = 50 ; - char* firstBuffer = new char[size](); - char* secondBuffer = new char[size](); - - Fwd url1, url2; - - bool lockHandlerExecuted = false; - auto lockOpenHandler = [&,url1, url2]( XRootDStatus &status ) mutable - { - url1 = GetFileUrl( "cb4aacf1-6f28-42f2-b68a-90a73460f424.dat" ); - url2 = GetFileUrl( dataFileName ); - lockHandlerExecuted = true; - }; - - std::future parallelresp, closeresp; - - Pipeline firstPipe = Open( firstFile, url1, readFlags ) - | Read( firstFile, offset, size, firstBuffer ) - | Close( firstFile ); - - Pipeline secondPipe = Open( secondFile, url2, readFlags ) - | Read( secondFile, offset, size, secondBuffer ) - | Close( secondFile ); - - Pipeline pipe = Open( lockFile, lockUrl, readFlags ) >> lockOpenHandler - | Parallel( firstPipe, secondPipe ) >> parallelresp - | Close( lockFile ) >> closeresp; - - XRootDStatus status = WaitFor( std::move( pipe ) ); - CPPUNIT_ASSERT(status.IsOK()); - - CPPUNIT_ASSERT(lockHandlerExecuted); - - try - { - parallelresp.get(); - closeresp.get(); - } - catch( std::exception &ex ) - { - CPPUNIT_ASSERT( false ); - } - - delete[] firstBuffer; - delete[] secondBuffer; - - //---------------------------------------------------------------------------- - // Remove lock file and data file - //---------------------------------------------------------------------------- - f = new File(); - dataF = new File(); - - auto url = GetAddress(); - FileSystem fs( url ); - - auto lockRelativePath = GetPath(lockFileName); - auto dataRelativePath = GetPath(dataFileName); - - bool exec1 = false, exec2 = false; - Pipeline deletingPipe( Parallel( Rm( fs, lockRelativePath ) >> [&]( XRootDStatus &status ){ CPPUNIT_ASSERT_XRDST( status ); exec1 = true; }, - Rm( fs, dataRelativePath ) >> [&]( XRootDStatus &status ){ CPPUNIT_ASSERT_XRDST( status ); exec2 = true; } ) ); - CPPUNIT_ASSERT_XRDST( WaitFor( std::move( deletingPipe ) ) ); - - CPPUNIT_ASSERT( exec1 ); - CPPUNIT_ASSERT( exec2 ); - - delete f; - delete dataF; - - //---------------------------------------------------------------------------- - // Test the policies - //---------------------------------------------------------------------------- - std::string url_exists = "/data/1db882c8-8cd6-4df1-941f-ce669bad3458.dat"; - std::string not_exists = "/data/blablabla.txt"; - CPPUNIT_ASSERT_XRDST( WaitFor( Parallel( Stat( fs, url_exists ), Stat( fs, url_exists ) ).Any() ) ); - - std::string also_exists = "/data/3c9a9dd8-bc75-422c-b12c-f00604486cc1.dat"; - CPPUNIT_ASSERT_XRDST( WaitFor( Parallel( Stat( fs, url_exists ), - Stat( fs, also_exists ), - Stat( fs, not_exists ) ).Some( 2 ) ) ); - std::atomic errcnt( 0 ); - std::atomic okcnt( 0 ); - - auto hndl = [&]( auto s, auto i ) - { - if( s.IsOK() ) ++okcnt; - else ++errcnt; - }; - - CPPUNIT_ASSERT_XRDST( WaitFor( Parallel( Stat( fs, url_exists ) >> hndl, - Stat( fs, also_exists ) >> hndl, - Stat( fs, not_exists ) >> hndl ).AtLeast( 1 ) ) ); - CPPUNIT_ASSERT( okcnt == 2 && errcnt == 1 ); -} - - -void WorkflowTest::FileSystemWorkflowTest(){ - using namespace XrdCl; - - TestingHandler mkDirHandler; - TestingHandler locateHandler; - TestingHandler moveHandler; - TestingHandler secondLocateHandler; - TestingHandler removeHandler; - - auto url = GetAddress(); - FileSystem fs( url ); - - std::string newDirUrl = GetPath("sourceDirectory"); - std::string destDirUrl = GetPath("destDirectory"); - - auto noneFlags = OpenFlags::None; - - Pipeline fsPipe = MkDir( fs, newDirUrl, MkDirFlags::None, Access::None ) >> mkDirHandler - | Locate( fs, newDirUrl, noneFlags ) >> locateHandler - | Mv( fs, newDirUrl, destDirUrl ) >> moveHandler - | Locate( fs, destDirUrl, OpenFlags::Refresh ) >> secondLocateHandler - | RmDir( fs, destDirUrl ) >> removeHandler; - - Pipeline pipe( std::move( fsPipe) ); - - XRootDStatus status = WaitFor( std::move( pipe ) ); - CPPUNIT_ASSERT(status.IsOK()); - - CPPUNIT_ASSERT(mkDirHandler.Executed()); - CPPUNIT_ASSERT(locateHandler.Executed()); - CPPUNIT_ASSERT(moveHandler.Executed()); - CPPUNIT_ASSERT(secondLocateHandler.Executed()); - CPPUNIT_ASSERT(removeHandler.Executed()); -} - - -void WorkflowTest::MixedWorkflowTest(){ - using namespace XrdCl; - - const size_t nbFiles = 2; - - FileSystem fs( GetAddress() ); - File file[nbFiles]; - - auto flags = OpenFlags::Write | OpenFlags::Delete | OpenFlags::Update; - - std::string dirName = "tempDir"; - std::string dirPath = GetPath( dirName ); - - std::string firstFileName = dirName + "/firstFile"; - std::string secondFileName = dirName + "/secondFile"; - std::string url[nbFiles] = { GetFileUrl(firstFileName), GetFileUrl(secondFileName) }; - - std::string path[nbFiles] = { GetPath(firstFileName), GetPath(secondFileName) }; - - std::string content[nbFiles] = { "First file content", "Second file content" }; - char* text[nbFiles] = { const_cast(content[0].c_str()), const_cast(content[1].c_str()) }; - size_t length[nbFiles] = { content[0].size(), content[1].size() }; - - - Fwd size[nbFiles]; - Fwd buffer[nbFiles]; - - std::future ftr[nbFiles]; - - bool cleaningHandlerExecuted = false; - auto cleaningHandler = [&](XRootDStatus &status, LocationInfo& info) - { - LocationInfo::Iterator it; - for( it = info.Begin(); it != info.End(); ++it ) - { - auto url = URL(it->GetAddress()); - FileSystem fs(url); - auto st = fs.RmDir(dirPath); - CPPUNIT_ASSERT(st.IsOK()); - } - cleaningHandlerExecuted = true; - }; - - std::vector fileWorkflows; - for( size_t i = 0; i < nbFiles; ++i ) - { - auto &&operation = Open( file[i], url[i], flags ) - | Write( file[i], 0, length[i], text[i] ) - | Sync( file[i] ) - | Stat( file[i], true ) >> [size, buffer, i]( XRootDStatus &status, StatInfo &info ) mutable - { - CPPUNIT_ASSERT_XRDST( status ); - size[i] = info.GetSize(); - buffer[i] = new char[*size[i]]; - } - | Read( file[i], 0, size[i], buffer[i] ) >> ftr[i] - | Close( file[i] ); - fileWorkflows.emplace_back( operation ); - } - - Pipeline pipe = MkDir( fs, dirPath, MkDirFlags::None, Access::None ) >> []( XRootDStatus &status ){ CPPUNIT_ASSERT_XRDST( status ); } - | Parallel( fileWorkflows ) - | Rm( fs, path[0] ) - | Rm( fs, path[1] ) - | DeepLocate( fs, dirPath, OpenFlags::Refresh ) >> cleaningHandler; - - XRootDStatus status = WaitFor( std::move( pipe ) ); - CPPUNIT_ASSERT_XRDST( status ); - - for( size_t i = 0; i < nbFiles; ++i ) - { - ChunkInfo chunk = ftr[i].get(); - char *buffer = reinterpret_cast( chunk.buffer ); - std::string result( buffer, chunk.length ); - delete[] buffer; - CPPUNIT_ASSERT( result == content[i] ); - } - - CPPUNIT_ASSERT(cleaningHandlerExecuted); -} - - -void WorkflowTest::WorkflowWithFutureTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "DataPath", dataPath ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - std::string filePath = dataPath + "/cb4aacf1-6f28-42f2-b68a-90a73460f424.dat"; - std::string fileUrl = address + "/"; - fileUrl += filePath; - - //---------------------------------------------------------------------------- - // Fetch some data and checksum - //---------------------------------------------------------------------------- - const uint32_t MB = 1024*1024; - char *expected = new char[40*MB]; - char *buffer = new char[40*MB]; - uint32_t bytesRead = 0; - File f; - - //---------------------------------------------------------------------------- - // Open and Read and Close in standard way - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT_XRDST( f.Open( fileUrl, OpenFlags::Read ) ); - CPPUNIT_ASSERT_XRDST( f.Read( 10*MB, 40*MB, expected, bytesRead ) ); - CPPUNIT_ASSERT( bytesRead == 40*MB ); - CPPUNIT_ASSERT_XRDST( f.Close() ); - - //---------------------------------------------------------------------------- - // Now do the test - //---------------------------------------------------------------------------- - File file; - std::future ftr; - Pipeline pipeline = Open( file, fileUrl, OpenFlags::Read ) | Read( file, 10*MB, 40*MB, buffer ) >> ftr | Close( file ); - std::future status = Async( std::move( pipeline ) ); - - try - { - ChunkInfo result = ftr.get(); - CPPUNIT_ASSERT( result.length = bytesRead ); - CPPUNIT_ASSERT( strncmp( expected, (char*)result.buffer, bytesRead ) == 0 ); - } - catch( PipelineException &ex ) - { - CPPUNIT_ASSERT( false ); - } - - CPPUNIT_ASSERT_XRDST( status.get() ) - - delete[] expected; - delete[] buffer; -} - -void WorkflowTest::XAttrWorkflowTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - Env *testEnv = TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "DataPath", dataPath ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - std::string filePath = dataPath + "/cb4aacf1-6f28-42f2-b68a-90a73460f424.dat"; - std::string fileUrl = address + "/"; - fileUrl += filePath; - - //---------------------------------------------------------------------------- - // Now do the test - //---------------------------------------------------------------------------- - std::string xattr_name = "xrd.name"; - std::string xattr_value = "ala ma kota"; - File file1, file2; - - // set extended attribute - Pipeline set = Open( file1, fileUrl, OpenFlags::Write ) - | SetXAttr( file1, xattr_name, xattr_value ) - | Close( file1 ); - CPPUNIT_ASSERT_XRDST( WaitFor( std::move( set ) ) ); - - // read and delete the extended attribute - std::future rsp1; - - Pipeline get_del = Open( file2, fileUrl, OpenFlags::Update ) - | GetXAttr( file2, xattr_name ) >> rsp1 - | DelXAttr( file2, xattr_name ) - | Close( file2 ); - - CPPUNIT_ASSERT_XRDST( WaitFor( std::move( get_del ) ) ); - - try - { - CPPUNIT_ASSERT( xattr_value == rsp1.get() ); - } - catch( PipelineException &ex ) - { - CPPUNIT_ASSERT( false ); - } - - //---------------------------------------------------------------------------- - // Test the bulk operations - //---------------------------------------------------------------------------- - std::vector names{ "xrd.name1", "xrd.name2" }; - std::vector attrs; - attrs.push_back( xattr_t( names[0], "ala ma kota" ) ); - attrs.push_back( xattr_t( names[1], "ela nic nie ma" ) ); - File file3, file4; - - // set extended attributes - Pipeline set_bulk = Open( file3, fileUrl, OpenFlags::Write ) - | SetXAttr( file3, attrs ) - | Close( file3 ); - CPPUNIT_ASSERT_XRDST( WaitFor( std::move( set_bulk ) ) ); - - // read and delete the extended attribute - Pipeline get_del_bulk = Open( file4, fileUrl, OpenFlags::Update ) - | ListXAttr( file4 ) >> - [&]( XRootDStatus &status, std::vector &rsp ) - { - CPPUNIT_ASSERT_XRDST( status ); - CPPUNIT_ASSERT( rsp.size() == attrs.size() ); - for( size_t i = 0; i < rsp.size(); ++i ) - { - auto itr = std::find_if( attrs.begin(), attrs.end(), - [&]( xattr_t &a ){ return std::get<0>( a ) == rsp[i].name; } ); - CPPUNIT_ASSERT( itr != attrs.end() ); - CPPUNIT_ASSERT( std::get<1>( *itr ) == rsp[i].value ); - } - } - | DelXAttr( file4, names ) - | Close( file4 ); - - CPPUNIT_ASSERT_XRDST( WaitFor( std::move( get_del_bulk ) ) ); - - //---------------------------------------------------------------------------- - // Test FileSystem xattr - //---------------------------------------------------------------------------- - FileSystem fs( fileUrl ); - std::future rsp2; - - Pipeline pipeline = SetXAttr( fs, filePath, xattr_name, xattr_value ) - | GetXAttr( fs, filePath, xattr_name ) >> rsp2 - | ListXAttr( fs, filePath ) >> - [&]( XRootDStatus &status, std::vector &rsp ) - { - CPPUNIT_ASSERT_XRDST( status ); - CPPUNIT_ASSERT( rsp.size() == 1 ); - CPPUNIT_ASSERT( rsp[0].name == xattr_name ); - CPPUNIT_ASSERT( rsp[0].value == xattr_value ); - } - | DelXAttr( fs, filePath, xattr_name ); - - CPPUNIT_ASSERT_XRDST( WaitFor( std::move( pipeline ) ) ); - - try - { - CPPUNIT_ASSERT( xattr_value == rsp2.get() ); - } - catch( PipelineException &ex ) - { - CPPUNIT_ASSERT( false ); - } -} - -void WorkflowTest::MkDirAsyncTest() { - using namespace XrdCl; - - FileSystem fs( GetAddress() ); - - std::packaged_task mkdirTask{ - []( XrdCl::XRootDStatus &st ) { - if (!st.IsOK()) - throw XrdCl::PipelineException( st ); - }}; - - XrdCl::Access::Mode access = XrdCl::Access::Mode::UR | XrdCl::Access::Mode::UW | - XrdCl::Access::Mode::UX | XrdCl::Access::Mode::GR | - XrdCl::Access::Mode::GW | XrdCl::Access::Mode::GX; - - auto &&t = Async( MkDir( fs, "/data/MkDirAsyncTest", XrdCl::MkDirFlags::None, access ) >> mkdirTask | - RmDir( fs, "/data/MkDirAsyncTest" ) - ); - - CPPUNIT_ASSERT(t.get().status == stOK); -} - -void WorkflowTest::CheckpointTest() { - using namespace XrdCl; - - File f1; - const char data[] = "Murzynek Bambo w Afryce mieszka,\n" - "czarna ma skore ten nasz kolezka\n" - "Uczy sie pilnie przez cale ranki\n" - "Ze swej murzynskiej pierwszej czytanki."; - std::string url = "root://localhost//data/chkpttest.txt"; - - CPPUNIT_ASSERT_XRDST( WaitFor( Open( f1, url, OpenFlags::New | OpenFlags::Write ) | - Write( f1, 0, sizeof( data ), data ) | - Close( f1 ) ) ); - - //--------------------------------------------------------------------------- - // Update the file without commiting the checkpoint - //--------------------------------------------------------------------------- - File f2; - const char update[] = "Jan A Kowalski"; - - CPPUNIT_ASSERT_XRDST( WaitFor( Open( f2, url, OpenFlags::Update ) | - Checkpoint( f2, ChkPtCode::BEGIN ) | - ChkptWrt( f2, 0, sizeof( update ), update ) | - Close( f2 ) ) ); - - File f3; - char readout[sizeof( data )]; - // readout the data to see if the update was succesful (it shouldn't be) - CPPUNIT_ASSERT_XRDST( WaitFor( Open( f3, url, OpenFlags::Read ) | - Read( f3, 0, sizeof( readout ), readout ) | - Close( f3 ) ) ); - // we expect the data to be unchanged - CPPUNIT_ASSERT( strncmp( readout, data, sizeof( data ) ) == 0 ); - - //--------------------------------------------------------------------------- - // Update the file and commit the changes - //--------------------------------------------------------------------------- - File f4; - CPPUNIT_ASSERT_XRDST( WaitFor( Open( f4, url, OpenFlags::Update ) | - Checkpoint( f4, ChkPtCode::BEGIN ) | - ChkptWrt( f4, 0, sizeof( update ), update ) | - Checkpoint( f4, ChkPtCode::COMMIT ) | - Close( f4 ) ) ); - File f5; - // readout the data to see if the update was succesful (it shouldn't be) - CPPUNIT_ASSERT_XRDST( WaitFor( Open( f5, url, OpenFlags::Read ) | - Read( f5, 0, sizeof( readout ), readout ) | - Close( f5 ) ) ); - // we expect the data to be unchanged - CPPUNIT_ASSERT( strncmp( readout, update, sizeof( update ) ) == 0 ); - CPPUNIT_ASSERT( strncmp( readout + sizeof( update ), data + sizeof( update ), - sizeof( data ) - sizeof( update ) ) == 0 ); - - //--------------------------------------------------------------------------- - // Now clean up - //--------------------------------------------------------------------------- - FileSystem fs( url ); - CPPUNIT_ASSERT_XRDST( WaitFor( Rm( fs, "/data/chkpttest.txt" ) ) ); -} - diff --git a/tests/XrdClTests/PollerTest.cc b/tests/XrdClTests/PollerTest.cc deleted file mode 100644 index cb5ecfb7bfa..00000000000 --- a/tests/XrdClTests/PollerTest.cc +++ /dev/null @@ -1,280 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2011-2012 by European Organization for Nuclear Research (CERN) -// Author: Lukasz Janyst -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -//------------------------------------------------------------------------------ - -#include -#include "XrdCl/XrdClPoller.hh" -#include "Server.hh" -#include "Utils.hh" -#include "TestEnv.hh" -#include "CppUnitXrdHelpers.hh" -#include "XrdCl/XrdClURL.hh" -#include "XrdCl/XrdClUtils.hh" -#include "XrdCl/XrdClSocket.hh" - -#include - - -#include "XrdCl/XrdClPollerBuiltIn.hh" - -using namespace XrdClTests; - -//------------------------------------------------------------------------------ -// Declaration -//------------------------------------------------------------------------------ -class PollerTest: public CppUnit::TestCase -{ - public: - CPPUNIT_TEST_SUITE( PollerTest ); - CPPUNIT_TEST( FunctionTestBuiltIn ); - CPPUNIT_TEST_SUITE_END(); - void FunctionTestBuiltIn(); - void FunctionTest( XrdCl::Poller *poller ); -}; - -CPPUNIT_TEST_SUITE_REGISTRATION( PollerTest ); - -//------------------------------------------------------------------------------ -// Client handler -//------------------------------------------------------------------------------ -class RandomPumpHandler: public ClientHandler -{ - public: - //-------------------------------------------------------------------------- - // Pump some random data through the socket - //-------------------------------------------------------------------------- - virtual void HandleConnection( int socket ) - { - XrdCl::ScopedDescriptor scopetDesc( socket ); - XrdCl::Log *log = TestEnv::GetLog(); - - uint8_t packets = random() % 100; - uint16_t packetSize; - char buffer[50000]; - log->Debug( 1, "Sending %d packets to the client", packets ); - - for( int i = 0; i < packets; ++i ) - { - packetSize = random() % 50000; - log->Dump( 1, "Sending %d packet, %d bytes of data", i, packetSize ); - if( Utils::GetRandomBytes( buffer, packetSize ) != packetSize ) - { - log->Error( 1, "Unable to get %d bytes of random data", packetSize ); - return; - } - - if( ::write( socket, buffer, packetSize ) != packetSize ) - { - log->Error( 1, "Unable to send the %d bytes of random data", - packetSize ); - return; - } - UpdateSentData( buffer, packetSize ); - } - } -}; - -//------------------------------------------------------------------------------ -// Client handler factory -//------------------------------------------------------------------------------ -class RandomPumpHandlerFactory: public ClientHandlerFactory -{ - public: - virtual ClientHandler *CreateHandler() - { - return new RandomPumpHandler(); - } -}; - -//------------------------------------------------------------------------------ -// Socket listener -//------------------------------------------------------------------------------ -class SocketHandler: public XrdCl::SocketHandler -{ - public: - //-------------------------------------------------------------------------- - // Initializer - //-------------------------------------------------------------------------- - virtual void Initialize( XrdCl::Poller *poller ) - { - pPoller = poller; - } - - //-------------------------------------------------------------------------- - // Handle an event - //-------------------------------------------------------------------------- - virtual void Event( uint8_t type, - XrdCl::Socket *socket ) - { - //------------------------------------------------------------------------ - // Read event - //------------------------------------------------------------------------ - if( type & ReadyToRead ) - { - char buffer[50000]; - int desc = socket->GetFD(); - ssize_t ret = 0; - - while( 1 ) - { - char *current = buffer; - uint32_t spaceLeft = 50000; - while( (spaceLeft > 0) && - ((ret = ::read( desc, current, spaceLeft )) > 0) ) - { - current += ret; - spaceLeft -= ret; - } - - UpdateTransferMap( socket->GetSockName(), buffer, 50000-spaceLeft ); - - if( ret == 0 ) - { - pPoller->RemoveSocket( socket ); - return; - } - - if( ret < 0 ) - { - if( errno != EAGAIN && errno != EWOULDBLOCK ) - pPoller->EnableReadNotification( socket, false ); - return; - } - } - } - - //------------------------------------------------------------------------ - // Timeout - //------------------------------------------------------------------------ - if( type & ReadTimeOut ) - pPoller->RemoveSocket( socket ); - } - - //-------------------------------------------------------------------------- - // Update the checksums - //-------------------------------------------------------------------------- - void UpdateTransferMap( const std::string &sockName, - const void *buffer, - uint32_t size ) - { - //------------------------------------------------------------------------ - // Check if we have an entry in the map - //------------------------------------------------------------------------ - std::pair res; - Server::TransferMap::iterator it; - res = pMap.insert( std::make_pair( sockName, std::make_pair( 0, 0 ) ) ); - it = res.first; - if( res.second == true ) - { - it->second.first = 0; - it->second.second = Utils::ComputeCRC32( 0, 0 ); - } - - //------------------------------------------------------------------------ - // Update the entry - //------------------------------------------------------------------------ - it->second.first += size; - it->second.second = Utils::UpdateCRC32( it->second.second, buffer, size ); - } - - //-------------------------------------------------------------------------- - //! Get the stats of the received data - //-------------------------------------------------------------------------- - std::pair GetReceivedStats( - const std::string sockName ) const - { - Server::TransferMap::const_iterator it = pMap.find( sockName ); - if( it == pMap.end() ) - return std::make_pair( 0, 0 ); - return it->second; - } - - private: - Server::TransferMap pMap; - XrdCl::Poller *pPoller; -}; - -//------------------------------------------------------------------------------ -// Test the functionality of a poller -//------------------------------------------------------------------------------ -void PollerTest::FunctionTest( XrdCl::Poller *poller ) -{ - using XrdCl::Socket; - using XrdCl::URL; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - Server server( Server::Both ); - Socket s[3]; - CPPUNIT_ASSERT( server.Setup( 9999, 3, new RandomPumpHandlerFactory() ) ); - CPPUNIT_ASSERT( server.Start() ); - CPPUNIT_ASSERT( poller->Initialize() ); - CPPUNIT_ASSERT( poller->Start() ); - - //---------------------------------------------------------------------------- - // Connect the sockets - //---------------------------------------------------------------------------- - SocketHandler *handler = new SocketHandler(); - for( int i = 0; i < 3; ++i ) - { - CPPUNIT_ASSERT_XRDST( s[i].Initialize() ); - CPPUNIT_ASSERT_XRDST( s[i].Connect( "localhost", 9999 ) ); - CPPUNIT_ASSERT( poller->AddSocket( &s[i], handler ) ); - CPPUNIT_ASSERT( poller->EnableReadNotification( &s[i], true, 60 ) ); - CPPUNIT_ASSERT( poller->IsRegistered( &s[i] ) ); - } - - //---------------------------------------------------------------------------- - // All the business happens elsewhere so we have nothing better to do - // here that wait, otherwise server->stop will hang. - //---------------------------------------------------------------------------- - ::sleep(5); - - //---------------------------------------------------------------------------- - // Cleanup - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT( poller->Stop() ); - CPPUNIT_ASSERT( server.Stop() ); - CPPUNIT_ASSERT( poller->Finalize() ); - - std::pair stats[3]; - std::pair statsServ[3]; - for( int i = 0; i < 3; ++i ) - { - CPPUNIT_ASSERT( !poller->IsRegistered( &s[i] ) ); - stats[i] = handler->GetReceivedStats( s[i].GetSockName() ); - statsServ[i] = server.GetSentStats( s[i].GetSockName() ); - CPPUNIT_ASSERT( stats[i].first == statsServ[i].first ); - CPPUNIT_ASSERT( stats[i].second == statsServ[i].second ); - } - - for( int i = 0; i < 3; ++i ) - s[i].Close(); - - delete handler; -} - -//------------------------------------------------------------------------------ -// Test the functionality the built-in poller -//------------------------------------------------------------------------------ -void PollerTest::FunctionTestBuiltIn() -{ - XrdCl::Poller *poller = new XrdCl::PollerBuiltIn(); - FunctionTest( poller ); - delete poller; -} diff --git a/tests/XrdClTests/PostMasterTest.cc b/tests/XrdClTests/PostMasterTest.cc deleted file mode 100644 index e54a696c841..00000000000 --- a/tests/XrdClTests/PostMasterTest.cc +++ /dev/null @@ -1,581 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2011-2012 by European Organization for Nuclear Research (CERN) -// Author: Lukasz Janyst -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -//------------------------------------------------------------------------------ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "TestEnv.hh" -#include "CppUnitXrdHelpers.hh" - -using namespace XrdClTests; - -//------------------------------------------------------------------------------ -// Declaration -//------------------------------------------------------------------------------ -class PostMasterTest: public CppUnit::TestCase -{ - public: - CPPUNIT_TEST_SUITE( PostMasterTest ); - CPPUNIT_TEST( FunctionalTest ); - CPPUNIT_TEST( PingIPv6 ); - CPPUNIT_TEST( ThreadingTest ); - CPPUNIT_TEST( MultiIPConnectionTest ); - CPPUNIT_TEST_SUITE_END(); - void FunctionalTest(); - void ThreadingTest(); - void PingIPv6(); - void MultiIPConnectionTest(); -}; - -CPPUNIT_TEST_SUITE_REGISTRATION( PostMasterTest ); - -//------------------------------------------------------------------------------ -// Tear down the post master -//------------------------------------------------------------------------------ -namespace -{ - class PostMasterFetch - { - public: - PostMasterFetch() { } - ~PostMasterFetch() { } - XrdCl::PostMaster *Get() { - return XrdCl::DefaultEnv::GetPostMaster(); - } - XrdCl::PostMaster *Reset() { - XrdCl::PostMaster *pm = Get(); - pm->Stop(); - pm->Finalize(); - CPPUNIT_ASSERT( pm->Initialize() != 0 ); - CPPUNIT_ASSERT( pm->Start() != 0 ); - return pm; - } - }; -} - -//------------------------------------------------------------------------------ -// Message filter -//------------------------------------------------------------------------------ -class XrdFilter -{ - friend class SyncMsgHandler; - - public: - XrdFilter( unsigned char id0 = 0, unsigned char id1 = 0 ) - { - streamId[0] = id0; - streamId[1] = id1; - } - - virtual bool Filter( const XrdCl::Message *msg ) - { - ServerResponse *resp = (ServerResponse *)msg->GetBuffer(); - if( resp->hdr.streamid[0] == streamId[0] && - resp->hdr.streamid[1] == streamId[1] ) - return true; - return false; - } - - virtual uint16_t GetSid() const - { - return (((uint16_t)streamId[1] << 8) | (uint16_t)streamId[0]); - } - - unsigned char streamId[2]; -}; - -//------------------------------------------------------------------------------ -// Synchronous Message Handler -//------------------------------------------------------------------------------ -class SyncMsgHandler : public XrdCl::MsgHandler -{ - public: - SyncMsgHandler() : - sem( 0 ), request( nullptr ), response( nullptr ), expiration( 0 ) - { - } - - private: - - XrdFilter filter; - XrdSysSemaphore sem; - XrdCl::XRootDStatus status; - const XrdCl::Message *request; - std::shared_ptr response; - time_t expiration; - - public: - - //------------------------------------------------------------------------ - // Examine an incoming message, and decide on the action to be taken - //------------------------------------------------------------------------ - virtual uint16_t Examine( std::shared_ptr &msg ) - { - if( filter.Filter( msg.get() ) ) - { - response = msg; - return RemoveHandler; - } - return Ignore; - } - - //------------------------------------------------------------------------ - // Reexamine the incoming message, and decide on the action to be taken - //------------------------------------------------------------------------ - virtual uint16_t InspectStatusRsp() - { - return XrdCl::MsgHandler::Action::None; - } - - //------------------------------------------------------------------------ - // Get handler sid - //------------------------------------------------------------------------ - virtual uint16_t GetSid() const - { - return filter.GetSid(); - } - - //------------------------------------------------------------------------ - // Process the message if it was "taken" by the examine action - //------------------------------------------------------------------------ - virtual void Process() - { - sem.Post(); - }; - - //------------------------------------------------------------------------ - // Handle an event other that a message arrival - //------------------------------------------------------------------------ - virtual uint8_t OnStreamEvent( StreamEvent event, - XrdCl::XRootDStatus status ) - { - if( event == Ready ) - return 0; - this->status = status; - sem.Post(); - return RemoveHandler; - }; - - //------------------------------------------------------------------------ - // The requested action has been performed and the status is available - //------------------------------------------------------------------------ - virtual void OnStatusReady( const XrdCl::Message *message, - XrdCl::XRootDStatus status ) - { - request = message; - this->status = status; - if( !status.IsOK() ) - sem.Post(); - } - - //------------------------------------------------------------------------ - // Get a timestamp after which we give up - //------------------------------------------------------------------------ - virtual time_t GetExpiration() - { - return expiration; - } - - void SetExpiration( time_t e ) - { - expiration = e; - } - - XrdCl::XRootDStatus WaitFor( XrdCl::Message &rsp ) - { - sem.Wait(); - if( response ) - rsp = std::move( *response ); - return status; - } - - void SetFilter( unsigned char id0 = 0, unsigned char id1 = 0 ) - { - filter.streamId[0] = id0; - filter.streamId[1] = id1; - } -}; - -//------------------------------------------------------------------------------ -// Thread argument passing helper -//------------------------------------------------------------------------------ -struct ArgHelper -{ - XrdCl::PostMaster *pm; - int index; -}; - -//------------------------------------------------------------------------------ -// Post master test thread -//------------------------------------------------------------------------------ -void *TestThreadFunc( void *arg ) -{ - using namespace XrdCl; - - std::string address; - Env *testEnv = TestEnv::GetEnv(); - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - - ArgHelper *a = (ArgHelper*)arg; - URL host( address ); - - //---------------------------------------------------------------------------- - // Send the ping messages - //---------------------------------------------------------------------------- - SyncMsgHandler msgHandlers[100]; - Message msgs[100]; - time_t expires = time(0)+1200; - for( int i = 0; i < 100; ++i ) - { - msgs[i].Allocate( sizeof( ClientPingRequest ) ); - msgs[i].Zero(); - msgs[i].SetDescription( "kXR_ping ()" ); - ClientPingRequest *request = (ClientPingRequest *)msgs[i].GetBuffer(); - request->streamid[0] = a->index; - request->requestid = kXR_ping; - request->dlen = 0; - XRootDTransport::MarshallRequest( &msgs[i] ); - request->streamid[1] = i; - msgHandlers[i].SetFilter( a->index, i ); - msgHandlers[i].SetExpiration( expires ); - CPPUNIT_ASSERT_XRDST( a->pm->Send( host, &msgs[i], &msgHandlers[i], false, expires ) ); - } - - //---------------------------------------------------------------------------- - // Receive the answers - //---------------------------------------------------------------------------- - for( int i = 0; i < 100; ++i ) - { - XrdCl::Message msg; - CPPUNIT_ASSERT_XRDST( msgHandlers[i].WaitFor( msg ) ); - ServerResponse *resp = (ServerResponse *)msg.GetBuffer(); - CPPUNIT_ASSERT( resp != 0 ); - CPPUNIT_ASSERT( resp->hdr.status == kXR_ok ); - CPPUNIT_ASSERT( msg.GetSize() == 8 ); - } - return 0; -} - -//------------------------------------------------------------------------------ -// Threading test -//------------------------------------------------------------------------------ -void PostMasterTest::ThreadingTest() -{ - using namespace XrdCl; - PostMasterFetch pmfetch; - PostMaster *postMaster = pmfetch.Get(); - - pthread_t thread[100]; - ArgHelper helper[100]; - - for( int i = 0; i < 100; ++i ) - { - helper[i].pm = postMaster; - helper[i].index = i; - pthread_create( &thread[i], 0, TestThreadFunc, &helper[i] ); - } - - for( int i = 0; i < 100; ++i ) - pthread_join( thread[i], 0 ); -} - -//------------------------------------------------------------------------------ -// Test the functionality of a poller -//------------------------------------------------------------------------------ -void PostMasterTest::FunctionalTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize the stuff - //---------------------------------------------------------------------------- - Env *env = DefaultEnv::GetEnv(); - Env *testEnv = TestEnv::GetEnv(); - env->PutInt( "TimeoutResolution", 1 ); - env->PutInt( "ConnectionWindow", 15 ); - - PostMasterFetch pmfetch; - PostMaster *postMaster = pmfetch.Get(); - - std::string address; - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - - //---------------------------------------------------------------------------- - // Send a message and wait for the answer - //---------------------------------------------------------------------------- - time_t expires = ::time(0)+1200; - Message m1, m2; - URL host( address ); - - SyncMsgHandler msgHandler1; - msgHandler1.SetFilter( 1, 2 ); - msgHandler1.SetExpiration( expires ); - - m1.Allocate( sizeof( ClientPingRequest ) ); - m1.Zero(); - - ClientPingRequest *request = (ClientPingRequest *)m1.GetBuffer(); - request->streamid[0] = 1; - request->streamid[1] = 2; - request->requestid = kXR_ping; - request->dlen = 0; - XRootDTransport::MarshallRequest( &m1 ); - - CPPUNIT_ASSERT_XRDST( postMaster->Send( host, &m1, &msgHandler1, false, expires ) ); - - CPPUNIT_ASSERT_XRDST( msgHandler1.WaitFor( m2 ) ); - ServerResponse *resp = (ServerResponse *)m2.GetBuffer(); - CPPUNIT_ASSERT( resp != 0 ); - CPPUNIT_ASSERT( resp->hdr.status == kXR_ok ); - CPPUNIT_ASSERT( m2.GetSize() == 8 ); - - //---------------------------------------------------------------------------- - // Send out some stuff to a location where nothing listens - //---------------------------------------------------------------------------- - env->PutInt( "ConnectionWindow", 5 ); - env->PutInt( "ConnectionRetry", 3 ); - URL localhost1( "root://localhost:10101" ); - - SyncMsgHandler msgHandler2; - msgHandler2.SetFilter( 1, 2 ); - time_t shortexp = ::time(0) + 3; - msgHandler2.SetExpiration( shortexp ); - CPPUNIT_ASSERT_XRDST( postMaster->Send( localhost1, &m1, &msgHandler2, false, - shortexp ) ); - CPPUNIT_ASSERT_XRDST_NOTOK( msgHandler2.WaitFor( m2 ), errOperationExpired ); - - SyncMsgHandler msgHandler3; - msgHandler3.SetFilter( 1, 2 ); - msgHandler3.SetExpiration( expires ); - CPPUNIT_ASSERT_XRDST( postMaster->Send( localhost1, &m1, &msgHandler3, false, - expires ) ); - CPPUNIT_ASSERT_XRDST_NOTOK( msgHandler3.WaitFor( m2 ), errConnectionError ); - - //---------------------------------------------------------------------------- - // Test the transport queries - //---------------------------------------------------------------------------- - AnyObject nameObj, sidMgrObj; - Status st1, st2; - const char *name = 0; - - CPPUNIT_ASSERT_XRDST( postMaster->QueryTransport( host, - TransportQuery::Name, - nameObj ) ); - nameObj.Get( name ); - - CPPUNIT_ASSERT( name ); - CPPUNIT_ASSERT( !::strcmp( name, "XRootD" ) ); - - //---------------------------------------------------------------------------- - // Reinitialize and try to do something - //---------------------------------------------------------------------------- - env->PutInt( "LoadBalancerTTL", 5 ); - postMaster = pmfetch.Reset(); - - m2.Free(); - m1.Zero(); - - request = (ClientPingRequest *)m1.GetBuffer(); - request->streamid[0] = 1; - request->streamid[1] = 2; - request->requestid = kXR_ping; - request->dlen = 0; - XRootDTransport::MarshallRequest( &m1 ); - - SyncMsgHandler msgHandler4; - msgHandler4.SetFilter( 1, 2 ); - msgHandler4.SetExpiration( expires ); - CPPUNIT_ASSERT_XRDST( postMaster->Send( host, &m1, &msgHandler4, false, expires ) ); - - CPPUNIT_ASSERT_XRDST( msgHandler4.WaitFor( m2 ) ); - resp = (ServerResponse *)m2.GetBuffer(); - CPPUNIT_ASSERT( resp != 0 ); - CPPUNIT_ASSERT( resp->hdr.status == kXR_ok ); - CPPUNIT_ASSERT( m2.GetSize() == 8 ); - - //---------------------------------------------------------------------------- - // Sleep 10 secs waiting for iddle connection to be closed and see - // whether we can reconnect - //---------------------------------------------------------------------------- - sleep( 10 ); - SyncMsgHandler msgHandler5; - msgHandler5.SetFilter( 1, 2 ); - msgHandler5.SetExpiration( expires ); - CPPUNIT_ASSERT_XRDST( postMaster->Send( host, &m1, &msgHandler5, false, expires ) ); - - CPPUNIT_ASSERT_XRDST( msgHandler5.WaitFor( m2 ) ); - resp = (ServerResponse *)m2.GetBuffer(); - CPPUNIT_ASSERT( resp != 0 ); - CPPUNIT_ASSERT( resp->hdr.status == kXR_ok ); - CPPUNIT_ASSERT( m2.GetSize() == 8 ); -} - - -//------------------------------------------------------------------------------ -// Test the functionality of a poller -//------------------------------------------------------------------------------ -void PostMasterTest::PingIPv6() -{ - using namespace XrdCl; -#if 0 - //---------------------------------------------------------------------------- - // Initialize the stuff - //---------------------------------------------------------------------------- - PostMasterFetch pmfetch; - PostMaster *postMaster = pmfetch.Get(); - - //---------------------------------------------------------------------------- - // Build the message - //---------------------------------------------------------------------------- - Message m1, *m2 = 0; - XrdFilter f1( 1, 2 ); - URL localhost1( "root://[::1]" ); - URL localhost2( "root://[::127.0.0.1]" ); - - m1.Allocate( sizeof( ClientPingRequest ) ); - m1.Zero(); - - ClientPingRequest *request = (ClientPingRequest *)m1.GetBuffer(); - request->streamid[0] = 1; - request->streamid[1] = 2; - request->requestid = kXR_ping; - request->dlen = 0; - XRootDTransport::MarshallRequest( &m1 ); - - Status sc; - - //---------------------------------------------------------------------------- - // Send the message - localhost1 - //---------------------------------------------------------------------------- - sc = postMaster->Send( localhost1, &m1, false, 1200 ); - CPPUNIT_ASSERT( sc.IsOK() ); - - sc = postMaster->Receive( localhost1, m2, &f1, false, 1200 ); - CPPUNIT_ASSERT( sc.IsOK() ); - ServerResponse *resp = (ServerResponse *)m2->GetBuffer(); - CPPUNIT_ASSERT( resp != 0 ); - CPPUNIT_ASSERT( resp->hdr.status == kXR_ok ); - CPPUNIT_ASSERT( m2->GetSize() == 8 ); - - //---------------------------------------------------------------------------- - // Send the message - localhost2 - //---------------------------------------------------------------------------- - sc = postMaster->Send( localhost2, &m1, false, 1200 ); - CPPUNIT_ASSERT( sc.IsOK() ); - - sc = postMaster->Receive( localhost2, m2, &f1, 1200 ); - CPPUNIT_ASSERT( sc.IsOK() ); - resp = (ServerResponse *)m2->GetBuffer(); - CPPUNIT_ASSERT( resp != 0 ); - CPPUNIT_ASSERT( resp->hdr.status == kXR_ok ); - CPPUNIT_ASSERT( m2->GetSize() == 8 ); -#endif -} - -namespace -{ - //---------------------------------------------------------------------------- - // Create a ping message - //---------------------------------------------------------------------------- - XrdCl::Message *CreatePing( char streamID1, char streamID2 ) - { - using namespace XrdCl; - Message *m = new Message(); - m->Allocate( sizeof( ClientPingRequest ) ); - m->Zero(); - - ClientPingRequest *request = (ClientPingRequest *)m->GetBuffer(); - request->streamid[0] = streamID1; - request->streamid[1] = streamID2; - request->requestid = kXR_ping; - XRootDTransport::MarshallRequest( m ); - return m; - } -} - - -//------------------------------------------------------------------------------ -// Connection test -//------------------------------------------------------------------------------ -void PostMasterTest::MultiIPConnectionTest() -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize the stuff - //---------------------------------------------------------------------------- - Env *env = DefaultEnv::GetEnv(); - Env *testEnv = TestEnv::GetEnv(); - env->PutInt( "TimeoutResolution", 1 ); - env->PutInt( "ConnectionWindow", 5 ); - - PostMasterFetch pmfetch; - PostMaster *postMaster = pmfetch.Get(); - - std::string address; - CPPUNIT_ASSERT( testEnv->GetString( "MultiIPServerURL", address ) ); - - time_t expires = ::time(0)+1200; - URL url1( "nenexistent" ); - URL url2( address ); - URL url3( address ); - url2.SetPort( 1111 ); - url3.SetPort( 1099 ); - - //---------------------------------------------------------------------------- - // Sent ping to a nonexistent host - //---------------------------------------------------------------------------- - SyncMsgHandler msgHandler1; - msgHandler1.SetFilter( 1, 2 ); - msgHandler1.SetExpiration( expires ); - Message *m = CreatePing( 1, 2 ); - CPPUNIT_ASSERT_XRDST_NOTOK( postMaster->Send( url1, m, &msgHandler1, false, expires ), - errInvalidAddr ); - - //---------------------------------------------------------------------------- - // Try on the wrong port - //---------------------------------------------------------------------------- - SyncMsgHandler msgHandler2; - msgHandler2.SetFilter( 1, 2 ); - msgHandler2.SetExpiration( expires ); - Message m2; - - CPPUNIT_ASSERT_XRDST( postMaster->Send( url2, m, &msgHandler2, false, expires ) ); - CPPUNIT_ASSERT_XRDST_NOTOK( msgHandler2.WaitFor( m2 ), errConnectionError ); - - //---------------------------------------------------------------------------- - // Try on a good one - //---------------------------------------------------------------------------- - SyncMsgHandler msgHandler3; - msgHandler3.SetFilter( 1, 2 ); - msgHandler3.SetExpiration( expires ); - - CPPUNIT_ASSERT_XRDST( postMaster->Send( url3, m, &msgHandler3, false, expires ) ); - CPPUNIT_ASSERT_XRDST( msgHandler3.WaitFor( m2 ) ); - ServerResponse *resp = (ServerResponse *)m2.GetBuffer(); - CPPUNIT_ASSERT( resp != 0 ); - CPPUNIT_ASSERT( resp->hdr.status == kXR_ok ); - CPPUNIT_ASSERT( m2.GetSize() == 8 ); -} diff --git a/tests/XrdClTests/SocketTest.cc b/tests/XrdClTests/SocketTest.cc deleted file mode 100644 index e6a4516aafd..00000000000 --- a/tests/XrdClTests/SocketTest.cc +++ /dev/null @@ -1,307 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2011-2012 by European Organization for Nuclear Research (CERN) -// Author: Lukasz Janyst -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -//------------------------------------------------------------------------------ - -#include -#include -#include -#include -#include -#include "Server.hh" -#include "Utils.hh" -#include "TestEnv.hh" -#include "XrdCl/XrdClSocket.hh" -#include "XrdCl/XrdClUtils.hh" - -using namespace XrdClTests; - -//------------------------------------------------------------------------------ -// Mock socket for testing -//------------------------------------------------------------------------------ -struct MockSocket : public XrdCl::Socket -{ - public: - - MockSocket() : size( sizeof( ServerResponseHeader ) + sizeof( ServerResponseBody_Protocol ) ), - buffer( reinterpret_cast( &response ) ), offset( 0 ), - random_engine( std::chrono::system_clock::now().time_since_epoch().count() ), - retrygen( 0, 9 ), - retry_threshold( retrygen( random_engine ) ) - { - response.hdr.status = kXR_ok; - response.hdr.streamid[0] = 1; - response.hdr.streamid[1] = 2; - response.hdr.dlen = htonl( sizeof( ServerResponseBody_Protocol ) ); - - response.body.protocol.flags = 123; - response.body.protocol.pval = 4567; - response.body.protocol.secreq.rsvd = 'A'; - response.body.protocol.secreq.seclvl = 'B'; - response.body.protocol.secreq.secopt = 'C'; - response.body.protocol.secreq.secver = 'D'; - response.body.protocol.secreq.secvsz = 'E'; - response.body.protocol.secreq.theTag = 'F'; - response.body.protocol.secreq.secvec.reqindx = 'G'; - response.body.protocol.secreq.secvec.reqsreq = 'H'; - } - - virtual XrdCl::XRootDStatus Read( char *outbuf, size_t rdsize, int &bytesRead ) - { - size_t btsleft = size - offset; - if( btsleft == 0 || nodata() ) - return XrdCl::XRootDStatus( XrdCl::stOK, XrdCl::suRetry ); - - if( rdsize > btsleft ) - rdsize = btsleft; - - std::uniform_int_distribution sizegen( 0, rdsize ); - rdsize = sizegen( random_engine ); - - if( rdsize == 0 ) - return XrdCl::XRootDStatus( XrdCl::stOK, XrdCl::suRetry ); - - memcpy( outbuf, buffer + offset, rdsize ); - offset += rdsize; - bytesRead = rdsize; - - return XrdCl::XRootDStatus(); - } - - virtual XrdCl::XRootDStatus Send( const char *buffer, size_t size, int &bytesWritten ) - { - return XrdCl::XRootDStatus( XrdCl::stError, XrdCl::errNotSupported ); - } - - inline bool IsEqual( XrdCl::Message &msg ) - { - response.hdr.dlen = ntohl( response.hdr.dlen ); - bool ok = ( memcmp( msg.GetBuffer(), &response, size ) == 0 ); - response.hdr.dlen = htonl( response.hdr.dlen ); - return ok; - } - - private: - - inline bool nodata() - { - size_t doretry = retrygen( random_engine ); - return doretry > retry_threshold; - } - - ServerResponse response; - const size_t size; - char *buffer; - size_t offset; - - std::default_random_engine random_engine; - std::uniform_int_distribution retrygen; - const size_t retry_threshold; -}; - -//------------------------------------------------------------------------------ -// Client handler -//------------------------------------------------------------------------------ -class RandomHandler: public ClientHandler -{ - public: - virtual void HandleConnection( int socket ) - { - XrdCl::ScopedDescriptor scopedDesc( socket ); - XrdCl::Log *log = TestEnv::GetLog(); - - //------------------------------------------------------------------------ - // Pump some data - //------------------------------------------------------------------------ - uint8_t packets = random() % 100; - uint16_t packetSize; - char buffer[50000]; - log->Debug( 1, "Sending %d packets to the client", packets ); - - if( ::Utils::Write( socket, &packets, 1 ) != 1 ) - { - log->Error( 1, "Unable to send the packet count" ); - return; - } - - for( int i = 0; i < packets; ++i ) - { - packetSize = random() % 50000; - log->Dump( 1, "Sending %d packet, %d bytes of data", i, packetSize ); - if( Utils::GetRandomBytes( buffer, packetSize ) != packetSize ) - { - log->Error( 1, "Unable to get %d bytes of random data", packetSize ); - return; - } - - if( ::Utils::Write( socket, &packetSize, 2 ) != 2 ) - { - log->Error( 1, "Unable to send the packet size" ); - return; - } - if( ::Utils::Write( socket, buffer, packetSize ) != packetSize ) - { - log->Error( 1, "Unable to send the %d bytes of random data", - packetSize ); - return; - } - UpdateSentData( buffer, packetSize ); - } - - //------------------------------------------------------------------------ - // Receive some data - //------------------------------------------------------------------------ - if( ::Utils::Read( socket, &packets, 1 ) != 1 ) - { - log->Error( 1, "Unable to receive the packet count" ); - return; - } - - log->Debug( 1, "Receivng %d packets from the client", packets ); - - for( int i = 0; i < packets; ++i ) - { - if( ::Utils::Read( socket, &packetSize, 2 ) != 2 ) - { - log->Error( 1, "Unable to receive the packet size" ); - return; - } - - if ( ::Utils::Read( socket, buffer, packetSize ) != packetSize ) - { - log->Error( 1, "Unable to receive the %d bytes of data", - packetSize ); - return; - } - UpdateReceivedData( buffer, packetSize ); - log->Dump( 1, "Received %d bytes from the client", packetSize ); - } - } -}; - -//------------------------------------------------------------------------------ -// Client handler factory -//------------------------------------------------------------------------------ -class RandomHandlerFactory: public ClientHandlerFactory -{ - public: - virtual ClientHandler *CreateHandler() - { - return new RandomHandler(); - } -}; - -//------------------------------------------------------------------------------ -// Declaration -//------------------------------------------------------------------------------ -class SocketTest: public CppUnit::TestCase -{ - public: - CPPUNIT_TEST_SUITE( SocketTest ); - CPPUNIT_TEST( TransferTest ); - CPPUNIT_TEST_SUITE_END(); - void TransferTest(); -}; - -CPPUNIT_TEST_SUITE_REGISTRATION( SocketTest ); - -//------------------------------------------------------------------------------ -// Test the transfer -//------------------------------------------------------------------------------ -void SocketTest::TransferTest() -{ - using namespace XrdCl; - srandom( time(0) ); - Server serv( Server::Both ); - Socket sock; - - //---------------------------------------------------------------------------- - // Start up the server and connect to it - //---------------------------------------------------------------------------- - CPPUNIT_ASSERT( serv.Setup( 9999, 1, new RandomHandlerFactory() ) ); - CPPUNIT_ASSERT( serv.Start() ); - - CPPUNIT_ASSERT( sock.GetStatus() == Socket::Disconnected ); - CPPUNIT_ASSERT( sock.Initialize( AF_INET6 ).IsOK() ); - CPPUNIT_ASSERT( sock.Connect( "localhost", 9999 ).IsOK() ); - CPPUNIT_ASSERT( sock.GetStatus() == Socket::Connected ); - - //---------------------------------------------------------------------------- - // Get the number of packets - //---------------------------------------------------------------------------- - uint8_t packets; - uint32_t bytesTransmitted; - uint16_t packetSize; - Status sc; - char buffer[50000]; - uint64_t sentCounter = 0; - uint32_t sentChecksum = ::Utils::ComputeCRC32( 0, 0 ); - uint64_t receivedCounter = 0; - uint32_t receivedChecksum = ::Utils::ComputeCRC32( 0, 0 ); - sc = sock.ReadRaw( &packets, 1, 60, bytesTransmitted ); - CPPUNIT_ASSERT( sc.status == stOK ); - - //---------------------------------------------------------------------------- - // Read each packet - //---------------------------------------------------------------------------- - for( int i = 0; i < packets; ++i ) - { - sc = sock.ReadRaw( &packetSize, 2, 60, bytesTransmitted ); - CPPUNIT_ASSERT( sc.status == stOK ); - sc = sock.ReadRaw( buffer, packetSize, 60, bytesTransmitted ); - CPPUNIT_ASSERT( sc.status == stOK ); - receivedCounter += bytesTransmitted; - receivedChecksum = ::Utils::UpdateCRC32( receivedChecksum, buffer, - bytesTransmitted ); - } - - //---------------------------------------------------------------------------- - // Send the number of packets - //---------------------------------------------------------------------------- - packets = random() % 100; - - sc = sock.WriteRaw( &packets, 1, 60, bytesTransmitted ); - CPPUNIT_ASSERT( (sc.status == stOK) && (bytesTransmitted == 1) ); - - for( int i = 0; i < packets; ++i ) - { - packetSize = random() % 50000; - CPPUNIT_ASSERT( ::Utils::GetRandomBytes( buffer, packetSize ) == packetSize ); - - sc = sock.WriteRaw( (char *)&packetSize, 2, 60, bytesTransmitted ); - CPPUNIT_ASSERT( (sc.status == stOK) && (bytesTransmitted == 2) ); - sc = sock.WriteRaw( buffer, packetSize, 60, bytesTransmitted ); - CPPUNIT_ASSERT( (sc.status == stOK) && (bytesTransmitted == packetSize) ); - sentCounter += bytesTransmitted; - sentChecksum = ::Utils::UpdateCRC32( sentChecksum, buffer, - bytesTransmitted ); - } - - //---------------------------------------------------------------------------- - // Check the counters and the checksums - //---------------------------------------------------------------------------- - std::string socketName = sock.GetSockName(); - - sock.Close(); - CPPUNIT_ASSERT( serv.Stop() ); - - std::pair sent = serv.GetSentStats( socketName ); - std::pair received = serv.GetReceivedStats( socketName ); - CPPUNIT_ASSERT( sentCounter == received.first ); - CPPUNIT_ASSERT( receivedCounter == sent.first ); - CPPUNIT_ASSERT( sentChecksum == received.second ); - CPPUNIT_ASSERT( receivedChecksum == sent.second ); -} diff --git a/tests/XrdClTests/ThreadingTest.cc b/tests/XrdClTests/ThreadingTest.cc deleted file mode 100644 index 2a446ad84de..00000000000 --- a/tests/XrdClTests/ThreadingTest.cc +++ /dev/null @@ -1,348 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2011-2012 by European Organization for Nuclear Research (CERN) -// Author: Lukasz Janyst -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -//------------------------------------------------------------------------------ - -#include -#include "TestEnv.hh" -#include "Utils.hh" -#include "CppUnitXrdHelpers.hh" -#include "XrdCl/XrdClFile.hh" -#include "XrdCl/XrdClDefaultEnv.hh" -#include "XrdCl/XrdClUtils.hh" -#include -#include -#include -#include -#include -#include "XrdCks/XrdCksData.hh" - -//------------------------------------------------------------------------------ -// Thread helper struct -//------------------------------------------------------------------------------ -struct ThreadData -{ - ThreadData(): - file( 0 ), startOffset( 0 ), length( 0 ), checkSum( 0 ), - firstBlockChecksum(0) {} - XrdCl::File *file; - uint64_t startOffset; - uint64_t length; - uint32_t checkSum; - uint32_t firstBlockChecksum; -}; - -const uint32_t MB = 1024*1024; - -//------------------------------------------------------------------------------ -// Declaration -//------------------------------------------------------------------------------ -class ThreadingTest: public CppUnit::TestCase -{ - public: - typedef void (*TransferCallback)( ThreadData *data ); - CPPUNIT_TEST_SUITE( ThreadingTest ); - CPPUNIT_TEST( ReadTest ); - CPPUNIT_TEST( MultiStreamReadTest ); - CPPUNIT_TEST( ReadForkTest ); - CPPUNIT_TEST( MultiStreamReadForkTest ); - CPPUNIT_TEST( MultiStreamReadMonitorTest ); - CPPUNIT_TEST_SUITE_END(); - void ReadTestFunc( TransferCallback transferCallback ); - void ReadTest(); - void MultiStreamReadTest(); - void ReadForkTest(); - void MultiStreamReadForkTest(); - void MultiStreamReadMonitorTest(); -}; - -CPPUNIT_TEST_SUITE_REGISTRATION( ThreadingTest ); - - -//------------------------------------------------------------------------------ -// Reader thread -//------------------------------------------------------------------------------ -void *DataReader( void *arg ) -{ - using namespace XrdClTests; - - ThreadData *td = (ThreadData*)arg; - - uint64_t offset = td->startOffset; - uint64_t dataLeft = td->length; - uint64_t chunkSize = 0; - uint32_t bytesRead = 0; - char *buffer = new char[4*MB]; - - while( 1 ) - { - chunkSize = 4*MB; - if( chunkSize > dataLeft ) - chunkSize = dataLeft; - - if( chunkSize == 0 ) - break; - - CPPUNIT_ASSERT_XRDST( td->file->Read( offset, chunkSize, buffer, - bytesRead ) ); - - offset += bytesRead; - dataLeft -= bytesRead; - td->checkSum = Utils::UpdateCRC32( td->checkSum, buffer, bytesRead ); - } - - delete [] buffer; - - return 0; -} - -//------------------------------------------------------------------------------ -// Read test -//------------------------------------------------------------------------------ -void ThreadingTest::ReadTestFunc( TransferCallback transferCallback ) -{ - using namespace XrdCl; - - //---------------------------------------------------------------------------- - // Initialize - //---------------------------------------------------------------------------- - Env *testEnv = XrdClTests::TestEnv::GetEnv(); - - std::string address; - std::string dataPath; - - CPPUNIT_ASSERT( testEnv->GetString( "MainServerURL", address ) ); - CPPUNIT_ASSERT( testEnv->GetString( "DataPath", dataPath ) ); - - URL url( address ); - CPPUNIT_ASSERT( url.IsValid() ); - - std::string fileUrl[5]; - std::string path[5]; - path[0] = dataPath + "/1db882c8-8cd6-4df1-941f-ce669bad3458.dat"; - path[1] = dataPath + "/3c9a9dd8-bc75-422c-b12c-f00604486cc1.dat"; - path[2] = dataPath + "/7235b5d1-cede-4700-a8f9-596506b4cc38.dat"; - path[3] = dataPath + "/7e480547-fe1a-4eaf-a210-0f3927751a43.dat"; - path[4] = dataPath + "/89120cec-5244-444c-9313-703e4bee72de.dat"; - - for( int i = 0; i < 5; ++i ) - fileUrl[i] = address + "/" + path[i]; - - //---------------------------------------------------------------------------- - // Open and stat the files - //---------------------------------------------------------------------------- - ThreadData threadData[20]; - - for( int i = 0; i < 5; ++i ) - { - File *f = new File(); - StatInfo *si = 0; - CPPUNIT_ASSERT_XRDST( f->Open( fileUrl[i], OpenFlags::Read ) ); - CPPUNIT_ASSERT_XRDST( f->Stat( false, si ) ); - CPPUNIT_ASSERT( si ); - CPPUNIT_ASSERT( si->TestFlags( StatInfo::IsReadable ) ); - - uint64_t step = si->GetSize()/4; - - for( int j = 0; j < 4; ++j ) - { - threadData[j*5+i].file = f; - threadData[j*5+i].startOffset = j*step; - threadData[j*5+i].length = step; - threadData[j*5+i].checkSum = XrdClTests::Utils::GetInitialCRC32(); - - - //------------------------------------------------------------------------ - // Get the checksum of the first 4MB block at the startOffser - this - // will be verified by the forking test - //------------------------------------------------------------------------ - uint64_t offset = threadData[j*5+i].startOffset; - char *buffer = new char[4*MB]; - uint32_t bytesRead = 0; - - CPPUNIT_ASSERT_XRDST( f->Read( offset, 4*MB, buffer, bytesRead ) ); - CPPUNIT_ASSERT( bytesRead == 4*MB ); - threadData[j*5+i].firstBlockChecksum = - XrdClTests::Utils::ComputeCRC32( buffer, 4*MB ); - delete [] buffer; - } - - threadData[15+i].length = si->GetSize() - threadData[15+i].startOffset; - delete si; - } - - //---------------------------------------------------------------------------- - // Spawn the threads and wait for them to finish - //---------------------------------------------------------------------------- - pthread_t thread[20]; - for( int i = 0; i < 20; ++i ) - CPPUNIT_ASSERT_PTHREAD( pthread_create( &(thread[i]), 0, - ::DataReader, &(threadData[i]) ) ); - - if( transferCallback ) - (*transferCallback)( threadData ); - - for( int i = 0; i < 20; ++i ) - CPPUNIT_ASSERT_PTHREAD( pthread_join( thread[i], 0 ) ); - - //---------------------------------------------------------------------------- - // Glue up and compare the checksums - //---------------------------------------------------------------------------- - uint32_t checkSums[5]; - for( int i = 0; i < 5; ++i ) - { - //-------------------------------------------------------------------------- - // Calculate the local check sum - //-------------------------------------------------------------------------- - checkSums[i] = threadData[i].checkSum; - for( int j = 1; j < 4; ++j ) - { - checkSums[i] = XrdClTests::Utils::CombineCRC32( checkSums[i], - threadData[j*5+i].checkSum, - threadData[j*5+i].length ); - } - - char crcBuff[9]; - XrdCksData crc; crc.Set( &checkSums[i], 4 ); crc.Get( crcBuff, 9 ); - std::string transferSum = "zcrc32:"; transferSum += crcBuff; - - //-------------------------------------------------------------------------- - // Get the checksum - //-------------------------------------------------------------------------- - std::string remoteSum, lastUrl; - threadData[i].file->GetProperty( "LastURL", lastUrl ); - CPPUNIT_ASSERT_XRDST( Utils::GetRemoteCheckSum( - remoteSum, "zcrc32", lastUrl ) ); - CPPUNIT_ASSERT( remoteSum == transferSum ); - CPPUNIT_ASSERT_MESSAGE( path[i], remoteSum == transferSum ); - } - - //---------------------------------------------------------------------------- - // Close the files - //---------------------------------------------------------------------------- - for( int i = 0; i < 5; ++i ) - { - CPPUNIT_ASSERT_XRDST( threadData[i].file->Close() ); - delete threadData[i].file; - } -} - - -//------------------------------------------------------------------------------ -// Read test -//------------------------------------------------------------------------------ -void ThreadingTest::ReadTest() -{ - ReadTestFunc(0); -} - -//------------------------------------------------------------------------------ -// Multistream read test -//------------------------------------------------------------------------------ -void ThreadingTest::MultiStreamReadTest() -{ - XrdCl::Env *env = XrdCl::DefaultEnv::GetEnv(); - env->PutInt( "SubStreamsPerChannel", 4 ); - ReadTestFunc(0); -} - -//------------------------------------------------------------------------------ -// Child - read some data from each of the open files and close them -//------------------------------------------------------------------------------ -int runChild( ThreadData *td ) -{ - XrdCl::Log *log = XrdClTests::TestEnv::GetLog(); - log->Debug( 1, "Running the child" ); - - for( int i = 0; i < 20; ++i ) - { - uint64_t offset = td[i].startOffset; - char *buffer = new char[4*MB]; - uint32_t bytesRead = 0; - - CPPUNIT_ASSERT_XRDST( td[i].file->Read( offset, 4*MB, buffer, bytesRead ) ); - CPPUNIT_ASSERT( bytesRead == 4*MB ); - CPPUNIT_ASSERT( td[i].firstBlockChecksum == - XrdClTests::Utils::ComputeCRC32( buffer, 4*MB ) ); - delete [] buffer; - } - - for( int i = 0; i < 5; ++i ) - { - CPPUNIT_ASSERT_XRDST( td[i].file->Close() ); - delete td[i].file; - } - - return 0; -} - -//------------------------------------------------------------------------------ -// Forking function -//------------------------------------------------------------------------------ -void forkAndRead( ThreadData *data ) -{ - XrdCl::Log *log = XrdClTests::TestEnv::GetLog(); - for( int chld = 0; chld < 5; ++chld ) - { - sleep(10); - pid_t pid; - log->Debug( 1, "About to fork" ); - CPPUNIT_ASSERT_ERRNO( (pid=fork()) != -1 ); - - if( !pid ) _exit( runChild( data ) ); - - log->Debug( 1, "Forked successfully, pid of the child: %d", pid ); - int status; - log->Debug( 1, "Waiting for the child" ); - CPPUNIT_ASSERT_ERRNO( waitpid( pid, &status, 0 ) != -1 ); - log->Debug( 1, "Wait done, status: %d", status ); - CPPUNIT_ASSERT( WIFEXITED( status ) ); - CPPUNIT_ASSERT( WEXITSTATUS( status ) == 0 ); - } -} - -//------------------------------------------------------------------------------ -// Read fork test -//------------------------------------------------------------------------------ -void ThreadingTest::ReadForkTest() -{ - XrdCl::Env *env = XrdCl::DefaultEnv::GetEnv(); - env->PutInt( "RunForkHandler", 1 ); - ReadTestFunc(&forkAndRead); -} - -//------------------------------------------------------------------------------ -// Multistream read fork test -//------------------------------------------------------------------------------ -void ThreadingTest::MultiStreamReadForkTest() -{ - XrdCl::Env *env = XrdCl::DefaultEnv::GetEnv(); - env->PutInt( "SubStreamsPerChannel", 4 ); - env->PutInt( "RunForkHandler", 1 ); - ReadTestFunc(&forkAndRead); -} - -//------------------------------------------------------------------------------ -// Multistream read monitor -//------------------------------------------------------------------------------ -void ThreadingTest::MultiStreamReadMonitorTest() -{ - XrdCl::Env *env = XrdCl::DefaultEnv::GetEnv(); - env->PutString( "ClientMonitor", "./libXrdClTestMonitor.so" ); - env->PutString( "ClientMonitorParam", "TestParam" ); - env->PutInt( "SubStreamsPerChannel", 4 ); - ReadTestFunc(0); -} diff --git a/tests/XrdClTests/UtilsTest.cc b/tests/XrdClTests/UtilsTest.cc deleted file mode 100644 index 7bec1130f41..00000000000 --- a/tests/XrdClTests/UtilsTest.cc +++ /dev/null @@ -1,264 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2011-2012 by European Organization for Nuclear Research (CERN) -// Author: Lukasz Janyst -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -// -// In applying this licence, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -//------------------------------------------------------------------------------ - -#include -#include "CppUnitXrdHelpers.hh" -#include "XrdCl/XrdClAnyObject.hh" -#include "XrdCl/XrdClTaskManager.hh" -#include "XrdCl/XrdClSIDManager.hh" -#include "XrdCl/XrdClPropertyList.hh" - -//------------------------------------------------------------------------------ -// Declaration -//------------------------------------------------------------------------------ -class UtilsTest: public CppUnit::TestCase -{ - public: - CPPUNIT_TEST_SUITE( UtilsTest ); - CPPUNIT_TEST( AnyTest ); - CPPUNIT_TEST( TaskManagerTest ); - CPPUNIT_TEST( SIDManagerTest ); - CPPUNIT_TEST( PropertyListTest ); - CPPUNIT_TEST_SUITE_END(); - void AnyTest(); - void TaskManagerTest(); - void SIDManagerTest(); - void PropertyListTest(); -}; - -CPPUNIT_TEST_SUITE_REGISTRATION( UtilsTest ); - -class A -{ - public: - A( bool &st ): a(0.0), stat(st) {} - ~A() { stat = true; } - double a; - bool &stat; -}; - -class B -{ - public: - int b; -}; - -//------------------------------------------------------------------------------ -// Any test -//------------------------------------------------------------------------------ -void UtilsTest::AnyTest() -{ - bool destructorCalled1 = false; - bool destructorCalled2 = false; - bool destructorCalled3 = false; - A *a1 = new A( destructorCalled1 ); - A *a2 = new A( destructorCalled2 ); - A *a3 = new A( destructorCalled3 ); - A *a4 = 0; - B *b = 0; - - XrdCl::AnyObject *any1 = new XrdCl::AnyObject(); - XrdCl::AnyObject *any2 = new XrdCl::AnyObject(); - XrdCl::AnyObject *any3 = new XrdCl::AnyObject(); - XrdCl::AnyObject *any4 = new XrdCl::AnyObject(); - - any1->Set( a1 ); - any1->Get( b ); - any1->Get( a4 ); - CPPUNIT_ASSERT( !b ); - CPPUNIT_ASSERT( a4 ); - CPPUNIT_ASSERT( any1->HasOwnership() ); - - delete any1; - CPPUNIT_ASSERT( destructorCalled1 ); - - any2->Set( a2 ); - any2->Set( (int*)0 ); - delete any2; - CPPUNIT_ASSERT( !destructorCalled2 ); - delete a2; - - any3->Set( a3, false ); - CPPUNIT_ASSERT( !any3->HasOwnership() ); - delete any3; - CPPUNIT_ASSERT( !destructorCalled3 ); - delete a3; - - // test destruction of an empty object - delete any4; -} - -//------------------------------------------------------------------------------ -// Some tasks that do something -//------------------------------------------------------------------------------ -class TestTask1: public XrdCl::Task -{ - public: - TestTask1( std::vector &runs ): pRuns( runs ) - { - SetName( "TestTask1" ); - } - virtual time_t Run( time_t now ) - { - pRuns.push_back( now ); - return 0; - } - private: - std::vector &pRuns; -}; - -class TestTask2: public XrdCl::Task -{ - public: - TestTask2( std::vector &runs ): pRuns( runs ) - { - SetName( "TestTask2" ); - } - - virtual time_t Run( time_t now ) - { - pRuns.push_back( now ); - if( pRuns.size() >= 5 ) - return 0; - return now+2; - } - private: - std::vector &pRuns; -}; - -//------------------------------------------------------------------------------ -// Task Manager test -//------------------------------------------------------------------------------ -void UtilsTest::TaskManagerTest() -{ - using namespace XrdCl; - - std::vector runs1, runs2; - Task *tsk1 = new TestTask1( runs1 ); - Task *tsk2 = new TestTask2( runs2 ); - - TaskManager taskMan; - CPPUNIT_ASSERT( taskMan.Start() ); - - time_t now = ::time(0); - taskMan.RegisterTask( tsk1, now+2 ); - taskMan.RegisterTask( tsk2, now+1 ); - - ::sleep( 6 ); - taskMan.UnregisterTask( tsk2 ); - - ::sleep( 2 ); - - CPPUNIT_ASSERT( runs1.size() == 1 ); - CPPUNIT_ASSERT( runs2.size() == 3 ); - CPPUNIT_ASSERT( taskMan.Stop() ); -} - -//------------------------------------------------------------------------------ -// SID Manager test -//------------------------------------------------------------------------------ -void UtilsTest::SIDManagerTest() -{ - using namespace XrdCl; - std::shared_ptr manager = SIDMgrPool::Instance().GetSIDMgr( "root://fake:1094//dir/file" ); - - uint8_t sid1[2]; - uint8_t sid2[2]; - uint8_t sid3[2]; - uint8_t sid4[2]; - uint8_t sid5[2]; - - CPPUNIT_ASSERT_XRDST( manager->AllocateSID( sid1 ) ); - CPPUNIT_ASSERT_XRDST( manager->AllocateSID( sid2 ) ); - manager->ReleaseSID( sid2 ); - CPPUNIT_ASSERT_XRDST( manager->AllocateSID( sid3 ) ); - CPPUNIT_ASSERT_XRDST( manager->AllocateSID( sid4 ) ); - CPPUNIT_ASSERT_XRDST( manager->AllocateSID( sid5 ) ); - - CPPUNIT_ASSERT( (sid1[0] != sid2[0]) || (sid1[1] != sid2[1]) ); - CPPUNIT_ASSERT( manager->NumberOfTimedOutSIDs() == 0 ); - manager->TimeOutSID( sid4 ); - manager->TimeOutSID( sid5 ); - CPPUNIT_ASSERT( manager->NumberOfTimedOutSIDs() == 2 ); - CPPUNIT_ASSERT( manager->IsTimedOut( sid3 ) == false ); - CPPUNIT_ASSERT( manager->IsTimedOut( sid1 ) == false ); - CPPUNIT_ASSERT( manager->IsTimedOut( sid4 ) == true ); - CPPUNIT_ASSERT( manager->IsTimedOut( sid5 ) == true ); - manager->ReleaseTimedOut( sid5 ); - CPPUNIT_ASSERT( manager->IsTimedOut( sid5 ) == false ); - manager->ReleaseAllTimedOut(); - CPPUNIT_ASSERT( manager->NumberOfTimedOutSIDs() == 0 ); -} - -//------------------------------------------------------------------------------ -// Property List test -//------------------------------------------------------------------------------ -void UtilsTest::PropertyListTest() -{ - using namespace XrdCl; - PropertyList l; - l.Set( "s1", "test string 1" ); - l.Set( "i1", 123456789123ULL ); - - uint64_t i1; - std::string s1; - - CPPUNIT_ASSERT( l.Get( "s1", s1 ) ); - CPPUNIT_ASSERT( s1 == "test string 1" ); - CPPUNIT_ASSERT( l.Get( "i1", i1 ) ); - CPPUNIT_ASSERT( i1 == 123456789123ULL ); - CPPUNIT_ASSERT( l.HasProperty( "s1" ) ); - CPPUNIT_ASSERT( !l.HasProperty( "s2" ) ); - CPPUNIT_ASSERT( l.HasProperty( "i1" ) ); - - for( int i = 0; i < 1000; ++i ) - l.Set( "vect_int", i, i+1000 ); - - int i; - int num; - for( i = 0; l.HasProperty( "vect_int", i ); ++i ) - { - CPPUNIT_ASSERT( l.Get( "vect_int", i, num ) ); - CPPUNIT_ASSERT( num = i+1000 ); - } - CPPUNIT_ASSERT( i == 1000 ); - - XRootDStatus st1, st2; - st1.SetErrorMessage( "test error message" ); - l.Set( "status", st1 ); - CPPUNIT_ASSERT( l.Get( "status", st2 ) ); - CPPUNIT_ASSERT( st2.status == st1.status ); - CPPUNIT_ASSERT( st2.code == st1.code ); - CPPUNIT_ASSERT( st2.errNo == st1.errNo ); - CPPUNIT_ASSERT( st2.GetErrorMessage() == st1.GetErrorMessage() ); - - std::vector v1, v2; - v1.push_back( "test string 1" ); - v1.push_back( "test string 2" ); - v1.push_back( "test string 3" ); - l.Set( "vector", v1 ); - CPPUNIT_ASSERT( l.Get( "vector", v2 ) ); - for( size_t i = 0; i < v1.size(); ++i ) - CPPUNIT_ASSERT( v1[i] == v2[i] ); -} diff --git a/tests/XrdClTests/XRootDProtocolHelper.cc b/tests/XrdClTests/XRootDProtocolHelper.cc deleted file mode 100644 index 93cc4654ffc..00000000000 --- a/tests/XrdClTests/XRootDProtocolHelper.cc +++ /dev/null @@ -1,118 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2011-2012 by European Organization for Nuclear Research (CERN) -// Author: Lukasz Janyst -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -//------------------------------------------------------------------------------ - -#include "XrdClTests/XRootDProtocolHelper.hh" -#include -#include - -//------------------------------------------------------------------------------ -// Handle XRootD Log-in -//------------------------------------------------------------------------------ -bool XRootDProtocolHelper::HandleLogin( int socket, XrdCl::Log *log ) -{ - //---------------------------------------------------------------------------- - // Handle the handshake - //---------------------------------------------------------------------------- - char handShakeBuffer[20]; - if( ::read( socket, handShakeBuffer, 20 ) != 20 ) - { - log->Error( 1, "Unable to read the handshake: %s", ::strerror( errno ) ); - return false; - } - - //---------------------------------------------------------------------------- - // Respond to the handshake - //---------------------------------------------------------------------------- - char serverHandShake[16]; memset( serverHandShake, 0, 16 ); - ServerInitHandShake *hs = (ServerInitHandShake *)(serverHandShake+4); - hs->msglen = ::htonl(8); - hs->protover = ::htonl( kXR_PROTOCOLVERSION ); - hs->msgval = ::htonl( kXR_DataServer ); - if( ::write( socket, serverHandShake, 16 ) != 16 ) - { - log->Error( 1, "Unable to write the handshake response: %s", - ::strerror( errno ) ); - return false; - } - - //---------------------------------------------------------------------------- - // Handle the protocol request - //---------------------------------------------------------------------------- - char protocolBuffer[24]; - if( ::read( socket, protocolBuffer, 24 ) != 24 ) - { - log->Error( 1, "Unable to read the protocol request: %s", ::strerror( errno ) ); - return false; - } - - //---------------------------------------------------------------------------- - // Respond to protocol - //---------------------------------------------------------------------------- - ServerResponse serverProtocol; memset( &serverProtocol, 0, 16 ); - serverProtocol.hdr.dlen = ::htonl( 8 ); - serverProtocol.body.protocol.pval = ::htonl( kXR_PROTOCOLVERSION ); - serverProtocol.body.protocol.flags = ::htonl( kXR_isServer ); - if( ::write( socket, &serverProtocol, 16 ) != 16 ) - { - log->Error( 1, "Unable to write the protocol response: %s", - ::strerror( errno ) ); - return false; - } - - //---------------------------------------------------------------------------- - // Handle the login - //---------------------------------------------------------------------------- - char loginBuffer[24]; - if( ::read( socket, loginBuffer, 24 ) != 24 ) - { - log->Error( 1, "Unable to read the login request: %s", ::strerror( errno ) ); - return false; - } - - //---------------------------------------------------------------------------- - // Respond to login - //---------------------------------------------------------------------------- - ServerResponse serverLogin; memset( &serverLogin, 0, 24 ); - serverLogin.hdr.dlen = ::htonl( 16 ); - if( ::write( socket, &serverLogin, 24 ) != 24 ) - { - log->Error( 1, "Unable to write the login response: %s", - ::strerror( errno ) ); - return false; - } - - return true; -} - -//------------------------------------------------------------------------------ -// Handle disconnection -//------------------------------------------------------------------------------ -bool XRootDProtocolHelper::HandleClose( int socket, XrdCl::Log *log ) -{ - return true; -} - -//------------------------------------------------------------------------------ -// Receive a message -//------------------------------------------------------------------------------ -bool XRootDProtocolHelper::GetMessage( XrdCl::Message *msg, int socket, - XrdCl::Log *log ) -{ - return true; -} - diff --git a/tests/XrdClTests/XRootDProtocolHelper.hh b/tests/XrdClTests/XRootDProtocolHelper.hh deleted file mode 100644 index c5e1d380465..00000000000 --- a/tests/XrdClTests/XRootDProtocolHelper.hh +++ /dev/null @@ -1,45 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2011-2012 by European Organization for Nuclear Research (CERN) -// Author: Lukasz Janyst -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -//------------------------------------------------------------------------------ - -#ifndef XROOTD_PROTOCOL_HELPER_HH -#define XROOTD_PROTOCOL_HELPER_HH - -#include -#include - -class XRootDProtocolHelper -{ - public: - //-------------------------------------------------------------------------- - //! Handle XRootD Log-in - //-------------------------------------------------------------------------- - bool HandleLogin( int socket, XrdCl::Log *log ); - - //-------------------------------------------------------------------------- - //! Handle disconnection - //-------------------------------------------------------------------------- - bool HandleClose( int socket, XrdCl::Log *log ); - - //-------------------------------------------------------------------------- - //! Receive a message - //-------------------------------------------------------------------------- - bool GetMessage( XrdCl::Message *msg, int socket, XrdCl::Log *log ); - private: -}; - -#endif // XROOTD_PROTOCOL_HELPER_HH diff --git a/tests/XrdClTests/cppunit.supp b/tests/XrdClTests/cppunit.supp deleted file mode 100644 index ad70bf372f9..00000000000 --- a/tests/XrdClTests/cppunit.supp +++ /dev/null @@ -1,17 +0,0 @@ -{ - CPPUnit typeinfo leak - Memcheck:Leak - fun:_Znwm - fun:_ZNSs4_Rep9_S_createEmmRKSaIcE - fun:_ZNSs12_S_constructIPcEES0_T_S1_RKSaIcESt20forward_iterator_tag - fun:_ZNSsC1ERKSsmm - fun:_ZN7CppUnit14TypeInfoHelper12getClassNameERKSt9type_info - fun:_ZN7CppUnit9TestNamerC1ERKSt9type_info -} - -{ - CPPUnit factory registry leak - Memcheck:Leak - fun:_Znwm - fun:_ZN7CppUnit19TestFactoryRegistry8makeTestEv -} diff --git a/tests/XrdClTests/printenv.sh b/tests/XrdClTests/printenv.sh deleted file mode 100755 index 3a5076f6078..00000000000 --- a/tests/XrdClTests/printenv.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -function printEnv() -{ - if [ $# -ne 1 ]; then - echo "[!] Invalid invocation; need a parameter" - return 1; - fi - - eval VALUE=\$$1 - printf "%-30s: " $1 - if test x"$VALUE" == x; then - echo "default" - else - echo $VALUE; - fi -} - -printEnv XRDTEST_MAINSERVERURL -printEnv XRDTEST_DISKSERVERURL -printEnv XRDTEST_DATAPATH -printEnv XRDTEST_LOCALFILE -printEnv XRDTEST_REMOTEFILE -printEnv XRDTEST_MULTIIPSERVERURL diff --git a/tests/XrdClTests/tls/CMakeLists.txt b/tests/XrdClTests/tls/CMakeLists.txt deleted file mode 100644 index 93b3e145a10..00000000000 --- a/tests/XrdClTests/tls/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ - - -#------------------------------------------------------------------------------- -# xrdcopy -#------------------------------------------------------------------------------- -if( NOT XRDCL_ONLY ) - -add_executable( - xrdcl-tls - xrdcl-tls.cc ) - -target_link_libraries( - xrdcl-tls - XrdCl - XrdPosix - XrdXml - XrdAppUtils - ${CMAKE_DL_LIBS} - ${CMAKE_THREAD_LIBS_INIT} ) - - -add_executable( - xrdsrv-tls - xrdsrv-tls.cc ) - -target_link_libraries( - xrdsrv-tls - XrdUtils - OpenSSL::SSL - ${CMAKE_THREAD_LIBS_INIT} ) - -endif() diff --git a/tests/XrdClTests/tls/README.md b/tests/XrdClTests/tls/README.md deleted file mode 100644 index 8029915d96c..00000000000 --- a/tests/XrdClTests/tls/README.md +++ /dev/null @@ -1,19 +0,0 @@ -The test requires a SSL certificate and private key. These can be generated -using the 'openssl' program using these steps: - -1. Generate the private key, this is what we normally keep secret: -```console - openssl genrsa -des3 -passout pass:x -out server.pass.key 2048 - openssl rsa -passin pass:x -in server.pass.key -out server.key - rm -f server.pass.key -``` -2. Next generate the CSR. We can leave the password empty when prompted - (because this is self-sign): -```console - openssl req -new -key server.key -out server.csr -``` -3. Next generate the self signed certificate: -```console - openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt - rm -f server.csr -``` diff --git a/tests/XrdClTests/tls/xrdcl-tls.cc b/tests/XrdClTests/tls/xrdcl-tls.cc deleted file mode 100644 index 0c0d1e2c191..00000000000 --- a/tests/XrdClTests/tls/xrdcl-tls.cc +++ /dev/null @@ -1,70 +0,0 @@ -#include "XrdCl/XrdClFile.hh" -#include "XrdCl/XrdClMessageUtils.hh" -#include "XrdCl/XrdClDefaultEnv.hh" - -#include - -int main( int argc, char *argv[] ) -{ -// XrdCl::Env *env = XrdCl::DefaultEnv::GetEnv(); -// env->PutInt( "SubStreamsPerChannel", 2 ); - - XrdCl::File f; - - std::cout << "Do open ... " << std::endl; - XrdCl::XRootDStatus st = f.Open( "roots://localhost//some/path/to/file", XrdCl::OpenFlags::Update ); - if( !st.IsOK() ) - { - std::cout << "Open failed" << std::endl; - std::cout << st.ToString() << std::endl; - return -1; - } - std::cout << "Open done!" << std::endl; - - std::cout << "Waiting for 5s ..." << std::endl; - sleep( 5 ); - std::cout << "... done waiting!" << std::endl; - - uint64_t offset = 20; - uint32_t size = 20; - uint32_t bytesRead = 0; - char buffer[size]; - - std::cout << "Do the 1st read ... " << std::endl; - st = f.Read( offset, size, buffer, bytesRead ); - if( !st.IsOK() ) - { - std::cout << "Read failed" << std::endl; - std::cout << st.ToString() << std::endl; - return -1; - } - std::cout << "Read done!" << std::endl; - - std::cout << "bytes read : " << bytesRead << std::endl; - std::cout << std::string( buffer, bytesRead ) << std::endl; - - std::cout << "Do the 2nd read ... " << std::endl; - st = f.Read( offset, size, buffer, bytesRead ); - if( !st.IsOK() ) - { - std::cout << "Read failed" << std::endl; - std::cout << st.ToString() << std::endl; - return -1; - } - std::cout << "Read done!" << std::endl; - - std::cout << "bytes read : " << bytesRead << std::endl; - std::cout << std::string( buffer, bytesRead ) << std::endl; - - std::cout << "Do close ... " << std::endl; - st = f.Close(); - if( !st.IsOK() ) - { - std::cout << "Close failed" << std::endl; - std::cout << st.ToString() << std::endl; - return -1; - } - std::cout << "Close done!" << std::endl; - - return 0; -} diff --git a/tests/XrdClTests/tls/xrdsrv-tls.cc b/tests/XrdClTests/tls/xrdsrv-tls.cc deleted file mode 100644 index 334de50c71d..00000000000 --- a/tests/XrdClTests/tls/xrdsrv-tls.cc +++ /dev/null @@ -1,698 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "XrdCl/XrdClXRootDTransport.hh" -#include "XProtocol/XProtocol.hh" -#include "XrdSys/XrdSysPlatform.hh" -#include "XrdTls/XrdTlsContext.hh" - - -struct StdIO -{ - void write( const std::string &str ) - { - std::unique_lock lck( mtx ); - std::cout << str << std::endl; - } - - private: - - std::mutex mtx; -} stdio; - -struct SocketIO -{ - SocketIO() : tlson( false ), socket( -1 ), ssl( 0 ) - { - } - - ~SocketIO() - { - if( ssl ) SSL_free( ssl ); - } - - void SetSocket( int socket ) - { - this->socket = socket; - } - - void TlsHandShake() - { - std::stringstream ss; - ss << std::endl << __func__ << std::endl << std::endl; - static XrdTlsContext tlsctx("server.crt", "server.key"); - - BIO *sbio = BIO_new_socket( socket, BIO_NOCLOSE ); - ssl = SSL_new( static_cast(tlsctx.Context()) ); - SSL_set_accept_state( ssl ); - SSL_set_bio( ssl, sbio, sbio ); - ss << "SSL_accept is waiting." << std::endl; - int rc = SSL_accept( ssl ); - int code = SSL_get_error( ssl, rc ); - - if( code != SSL_ERROR_NONE ) - { - char *str = ERR_error_string( code , 0 ); - std::cerr << "SSL_accept failed : code : " << code << std::endl; - std::cerr << str << std::endl; - exit( -1 ); - } - - tlson = true; - - ss << "SSL hand shake done!" << std::endl; - stdio.write( ss.str() ); - } - - int read( void *buffer, int size ) - { - int ret = 0; - - char *buff = static_cast( buffer ); - while ( size != 0 ) - { - int rc = 0; - if( tlson ) - rc = SSL_read( ssl, buff, size ); - else - rc = ::read( socket, buff, size ); - - if( rc <= 0 ) - break; - - ret += rc; - buff += rc; - size -= rc; - } - - return ret; - } - - int write( const void *buffer, int size ) - { - int rc = 0; - if( tlson ) - rc = SSL_write( ssl, buffer, size ); - else - rc = ::write( socket, buffer, size ); - - return rc; - } - - void renegotiate() - { - if( tlson ) - SSL_renegotiate( ssl ); - } - - private: - - bool tlson; - int socket; - SSL *ssl; - -} mainio, dataio; - -void die(const char *msg) { - perror(msg); - exit(1); -} - -void handle_error(const char *file, int lineno, const char *msg) { - fprintf(stderr, "** %s:%i %s\n", file, lineno, msg); - ERR_print_errors_fp(stderr); - exit(-1); -} - -#define int_error(msg) handle_error(__FILE__, __LINE__, msg) - -void DoInitHS( SocketIO &io ) -{ - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.status = kXR_ok; - respHdr.dlen = htonl( 2 * sizeof( kXR_int32 ) ); - io.write( &respHdr, sizeof(ServerResponseHeader) ); - - ServerInitHandShake hs; - memset( &hs, 0, sizeof( ServerInitHandShake ) ); - hs.protover = htonl( 0x500 ); - io.write( &hs.protover, sizeof( kXR_int32 ) ); - hs.msgval = htonl( kXR_DataServer ); - io.write( &hs.msgval, sizeof( kXR_int32 ) ); -} - -void HandleProtocolReq( SocketIO &io, ClientRequestHdr *hdr ) -{ - std::stringstream ss; - ss << __func__ << std::endl; - - ClientProtocolRequest *req = (ClientProtocolRequest*)hdr; - - ss << "Client protocol version : " << std::hex << ntohl(req->clientpv) << std::dec << std::endl; - ss << "Flags : " << (int)req->flags << std::endl; - ss << "Expect : " << (int)req->expect << std::endl; - stdio.write( ss.str() ); - - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.status = kXR_ok; - respHdr.dlen = htonl( sizeof( ServerResponseBody_Protocol ) ); - io.write( &respHdr, sizeof(ServerResponseHeader) ); - - kXR_int32 flags = kXR_DataServer | kXR_haveTLS | kXR_gotoTLS | kXR_tlsLogin;// | kXR_tlsData; - std::cout << "Server flags = " << flags << std::endl; - - ServerResponseBody_Protocol body; - body.pval = htonl( 0x500 ); - body.flags = htonl( flags ); - io.write( &body, sizeof(ServerResponseBody_Protocol) ); - - if( &io == &mainio && ( flags & kXR_tlsLogin ) ) - io.TlsHandShake(); - else if( &io == &dataio && (flags & kXR_tlsData ) ) - io.TlsHandShake(); -} - -void HandleLoginReq( SocketIO &io, ClientRequestHdr *hdr ) -{ - ClientLoginRequest *req = (ClientLoginRequest*) hdr; - - std::stringstream ss; - ss << __func__ << std::endl; - ss << "Client PID : " << std::dec << ntohl( req->pid ) << std::endl; - - char *buffer = new char[hdr->dlen]; - io.read( buffer, hdr->dlen ); - ss << "Token : " << std::string( buffer, hdr->dlen ) << std::endl; - stdio.write( ss.str() ); - delete[] buffer; - - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.status = kXR_ok; - respHdr.dlen = htonl( 16 ); - io.write( &respHdr, sizeof( ServerResponseHeader ) ); - - ServerResponseBody_Login body; - memset( body.sessid, 0, 16 ); - io.write( &body, 16 ); -} - -void HandleOpenReq( SocketIO &io, ClientRequestHdr *hdr ) -{ - ClientOpenRequest *req = (ClientOpenRequest*) hdr; - std::stringstream ss; - ss << __func__ << std::endl; - ss << "Open mode : 0x" << std::hex << ntohs( req->mode ) << std::dec << std::endl; - - static const std::string statstr = "ABCD 1024 0 0"; - - char *buffer = new char[req->dlen]; - io.read( buffer, req->dlen ); - ss << "Path : " << std::string( buffer, req->dlen ) << std::endl; - delete[] buffer; - - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.streamid[0] = req->streamid[0]; - respHdr.streamid[1] = req->streamid[1]; - respHdr.status = kXR_ok; - respHdr.dlen = htonl( 12 + statstr.size() ); - io.write( &respHdr, sizeof( ServerResponseHeader ) ); - - ServerResponseBody_Open body; - memset( body.fhandle, 0, 4 ); - memset( body.cptype, 0, 4 ); - body.cpsize = 0; - io.write( &body, 12 ); - - io.write( statstr.c_str(), statstr.size() ); - - ss << "Renegotiating session ..." << std::endl; - stdio.write( ss.str() ); - io.renegotiate(); //< test session re-negotiation !!! -} - -void HandleReadReq( SocketIO &io, ClientRequestHdr *hdr ) -{ - ClientReadRequest *req = (ClientReadRequest*) hdr; - std::stringstream ss; - ss << __func__ << " : " << ( &io == &dataio ? "data stream." : "control stream." ) << std::endl; - - req->rlen = ntohl( req->rlen ); - req->offset = ntohll( req->offset ); - ss << std::dec << "Read " << req->rlen << " bytes at " << req->offset << " offset" << std::endl; - - static const std::string readstr = "ala ma kota, a ola ma psa, a ela ma rybke"; - - kXR_char pathid = 0; - if( req->dlen ) - { - ss << "alen : " << req->dlen << std::endl; - char *buffer = new char[req->dlen]; - io.read( buffer, req->dlen ); - - read_args* rargs = (read_args*)buffer; - pathid = rargs->pathid; - ss << "Path ID : " << (int)rargs->pathid << std::endl; - delete[] buffer; - } - - int dlen = req->rlen > int( readstr.size() ) ? readstr.size() : req->rlen; - - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.streamid[0] = req->streamid[0]; - respHdr.streamid[1] = req->streamid[1]; - respHdr.status = kXR_ok; - respHdr.dlen = htonl( dlen ); - - // pick up the I/O based on the pathid - if( !pathid ) - { - mainio.write( &respHdr, sizeof( ServerResponseHeader ) ); - mainio.write( readstr.c_str(), dlen ); - } - else - { - ss << "Writing to data stream!" << std::endl; - dataio.write( &respHdr, sizeof( ServerResponseHeader ) ); - dataio.write( readstr.c_str(), dlen ); - } - - stdio.write( ss.str() ); -} - -void HandleCloseReq( SocketIO &io, ClientRequestHdr *hdr ) -{ - ClientReadRequest *req = (ClientReadRequest*) hdr; - std::stringstream ss; - ss << __func__ << std::endl; - stdio.write( ss.str() ); - - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.streamid[0] = req->streamid[0]; - respHdr.streamid[1] = req->streamid[1]; - respHdr.status = kXR_ok; - respHdr.dlen = 0; - io.write( &respHdr, sizeof( ServerResponseHeader ) ); -} - -void HandleBindReq( SocketIO &io, ClientRequestHdr *hdr ) -{ - ClientBindRequest *req = (ClientBindRequest*) hdr; - std::stringstream ss; - ss << __func__ << std::endl; - stdio.write( ss.str() ); - - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.streamid[0] = req->streamid[0]; - respHdr.streamid[1] = req->streamid[1]; - respHdr.status = kXR_ok; - respHdr.dlen = htonl( 1 ); - io.write( &respHdr, sizeof( ServerResponseHeader ) ); - - ServerResponseBody_Bind body; - body.substreamid = 1; - io.write( &body, sizeof( ServerResponseBody_Bind ) ); -} - - -int HandleRequest( SocketIO &io, int iterations ) -{ - char buffer[1024] = {0}; - std::fill( buffer, buffer + 1024, 'A' ); - int valread = io.read( buffer, 20 ); - std::stringstream ss; - ss << "valread : " << valread << std::endl; - if( valread != 20 ) return -1; - ClientInitHandShake *init = (ClientInitHandShake*)buffer; - ss << "First : " << ntohl( init->first ) << std::endl; - ss << "Second : " << ntohl( init->second ) << std::endl; - ss << "Third : " << ntohl( init->third ) << std::endl; - ss << "Fourth : " << ntohl( init->fourth ) << std::endl; - ss << "Fifth : " << ntohl( init->fifth ) << std::endl; - stdio.write( ss.str() ); - - DoInitHS( io ); - - int count = 0; - while( count < iterations ) - { - ++count; - - std::stringstream ss; - ss << std::endl; - ss << "Step : " << count << std::endl; - ss << "Waiting for client ..." << std::endl; - ss << "reading : " << sizeof( ClientRequestHdr ) << " bytes." << std::endl; - valread = io.read( buffer, sizeof( ClientRequestHdr ) ); - ss << "valread : " << valread << std::endl; - if( valread < 0 ) - { - return -1; - } - else if( valread < 8 ) - { - std::cout << "Got bogus header!" << std::endl; - std::cout << std::string( buffer, valread ) << std::endl; - return -1; - } - - ClientRequestHdr *hdr = (ClientRequestHdr*)buffer; - hdr->dlen = ntohl( hdr->dlen ); - hdr->requestid = ntohs( hdr->requestid ); - - ss << "Got request: " << hdr->requestid << std::endl; - stdio.write( ss.str() ); - - switch( hdr->requestid ) - { - case kXR_auth: - { - stdio.write( "Got kXR_auth!" ); - break; - } - - case kXR_query: - { - stdio.write( "Got kXR_query!" ); - break; - } - - case kXR_chmod: - { - stdio.write( "Got kXR_chmod!" ); - break; - } - - case kXR_close: - { - stdio.write( "Got kXR_close!" ); - HandleCloseReq( io, hdr ); - break; - } - - case kXR_dirlist: - { - stdio.write( "Got kXR_dirlist!" ); - break; - } - - case kXR_gpfile: - { - stdio.write( "Got kXR_gpfile!" ); - break; - } - - case kXR_protocol: - { - stdio.write( "Got kXR_protocol!" ); - HandleProtocolReq( io, hdr ); - break; - } - - case kXR_login: - { - stdio.write( "Got kXR_login!" ); - HandleLoginReq( io, hdr ); - break; - } - - case kXR_mkdir: - { - stdio.write( "Got kXR_mkdir!" ); - break; - } - - case kXR_mv: - { - stdio.write( "Got kXR_mv!" ); - break; - } - - case kXR_open: - { - stdio.write( "Got kXR_open!" ); - HandleOpenReq( io, hdr ); - break; - } - - case kXR_ping: - { - stdio.write( "Got kXR_ping!" ); - break; - } - - case kXR_chkpoint: - { - stdio.write( "Got kXR_chkpoint!" ); - break; - } - - case kXR_read: - { - stdio.write( "Got kXR_read!" ); - HandleReadReq( io, hdr ); - break; - } - - case kXR_rm: - { - stdio.write( "Got kXR_rm!" ); - break; - } - - case kXR_rmdir: - { - stdio.write( "Got kXR_rmdir!" ); - break; - } - - case kXR_sync: - { - stdio.write( "Got kXR_sync!" ); - break; - } - - case kXR_stat: - { - stdio.write( "Got kXR_stat!" ); - break; - } - - case kXR_set: - { - stdio.write( "Got kXR_set!" ); - break; - } - - case kXR_write: - { - stdio.write( "Got kXR_write!" ); - break; - } - - case kXR_fattr: - { - stdio.write( "Got kXR_fattr!" ); - break; - } - - case kXR_prepare: - { - stdio.write( "Got kXR_prepare!" ); - break; - } - - case kXR_statx: - { - stdio.write( "Got kXR_statx!" ); - break; - } - - case kXR_endsess: - { - stdio.write( "Got kXR_endsess!" ); - break; - } - - case kXR_bind: - { - stdio.write( "Got kXR_bind!" ); - HandleBindReq( io, hdr ); - break; - } - - case kXR_readv: - { - stdio.write( "Got kXR_readv!" ); - break; - } - - case kXR_pgwrite: - { - stdio.write( "Got kXR_pgwrite!" ); - break; - } - - case kXR_locate: - { - stdio.write( "Got kXR_locate!" ); - break; - } - - case kXR_truncate: - { - stdio.write( "Got kXR_truncate!" ); - break; - } - - case kXR_sigver: - { - stdio.write( "Got kXR_sigver!" ); - break; - } - - case kXR_pgread: - { - stdio.write( "Got kXR_pgread!" ); - break; - } - - case kXR_writev: - { - stdio.write( "Got kXR_writev!" ); - break; - } - }; - - } - - return 0; -} - - -void* control_stream( void *arg ) -{ - std::stringstream ss; - ss << '\n' << __func__ << '\n'; - stdio.write( ss.str() ); - - int socket = *(int*)arg; - mainio.SetSocket( socket ); - int rc = HandleRequest( mainio, 6 ); - return new int( rc ); -} - -void* data_stream( void *arg ) -{ - std::stringstream ss; - ss << '\n' << __func__ << '\n'; - stdio.write( ss.str() ); - - int socket = *(int*)arg; - dataio.SetSocket( socket ); - int rc = HandleRequest( dataio, 2 ); - return new int( rc ); -} - - -// TODO we need control thread and data thread !!! - -int main(int argc, char const *argv[]) -{ - int server_fd, new_socket; - struct sockaddr_in address; - int opt = 1; - int addrlen = sizeof(address); - - // Creating socket file descriptor - if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) - { - perror("socket failed"); - exit(EXIT_FAILURE); - } - - // Forcefully attaching socket to the port 8080 - if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, - &opt, sizeof(opt))) - { - perror("setsockopt"); - exit(EXIT_FAILURE); - } - address.sin_family = AF_INET; - address.sin_addr.s_addr = INADDR_ANY; - address.sin_port = htons( 1094 ); - - // Forcefully attaching socket to the port 8080 - if (bind(server_fd, (struct sockaddr *)&address, - sizeof(address))<0) - { - perror("bind failed"); - exit(EXIT_FAILURE); - } - if (listen(server_fd, 3) < 0) - { - perror("listen"); - exit(EXIT_FAILURE); - } - - pthread_t threads[2]; - - int count = 0; - while( count < 2 ) - { - std::stringstream ss; - ss << "Waiting to accept new TCP connection!" << std::endl; - if ((new_socket = accept(server_fd, (struct sockaddr *)&address, // TODO - (socklen_t*)&addrlen))<0) - { - perror("accept"); - exit(EXIT_FAILURE); - } - ss << "New TCP connection accepted!" << std::endl; - stdio.write( ss.str() ); - - if( count == 0 ) - pthread_create( &threads[count], 0, control_stream, &new_socket ); - else - pthread_create( &threads[count], 0, data_stream, &new_socket ); - ++count; - } - - count = 0; - while( count < 2 ) - { - void *ret = 0; - pthread_join( threads[count], &ret ); - std::unique_ptr rc( (int*)ret ); - if( *rc ) return *rc; - ++count; - } - - sleep( 2 ); - - std::cout << "The End." << std::endl; - - return 0; -} diff --git a/tests/XrdClTests/wrt/xrdsrv-dio.cc b/tests/XrdClTests/wrt/xrdsrv-dio.cc deleted file mode 100644 index 6ad7f83eecc..00000000000 --- a/tests/XrdClTests/wrt/xrdsrv-dio.cc +++ /dev/null @@ -1,602 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - - -#include "XrdCl/XrdClXRootDTransport.hh" -#include "XProtocol/XProtocol.hh" -#include "XrdSys/XrdSysPlatform.hh" -#include "XrdTls/XrdTlsContext.hh" - -struct StdIO -{ - void write( const std::string &str ) - { - std::unique_lock lck( mtx ); - std::cout << str << std::endl; - } - - private: - - std::mutex mtx; -} stdio; - -struct SocketIO -{ - SocketIO( int socket ) : socket( socket ) - { - } - - ~SocketIO() - { - } - - int read( void *buffer, int size ) - { - int ret = 0; - - char *buff = static_cast( buffer ); - while ( size != 0 ) - { - int rc = ::read( socket, buff, size ); - - if( rc <= 0 ) - break; - - ret += rc; - buff += rc; - size -= rc; - } - - return ret; - } - - int write( const void *buffer, int size ) - { - int rc = ::write( socket, buffer, size ); - return rc; - } - - void set_fn( const std::string &fn ) - { - this->fn = fn; - } - - const std::string& get_fn() - { - return fn; - } - - private: - int socket; - std::string fn; - -}; - -struct wrt_queue -{ - wrt_queue() : working( true ), worker( work, this ) - { - } - - ~wrt_queue() - { - working = false; - cv.notify_all(); - worker.join(); - } - - static void work( wrt_queue *myself ) - { - while( myself->working ) - { - wrt_request req = myself->pop(); - if( !myself->working ) return; - int rc = pwrite( req.fd, req.buf, req.len, req.off ); - if( rc < 0 ) - { - stdio.write( std::to_string( req.fd ) + ", " + std::to_string( errno ) ); - stdio.write( std::string( "Write failed: " ) + strerror( errno ) ); - } - free( req.buf ); - myself->done(); - } - } - - struct wrt_request - { - wrt_request( int fd, char *buf, size_t len, off_t off ) : - fd( fd ), buf( buf ), len( len ), off( off ) - { - } - - wrt_request() : fd( -1 ), buf( nullptr ), len( 0 ), off( 0 ){ } - - int fd; - char *buf; - size_t len; - off_t off; - }; - - void write( int fd, char *buf, size_t len, off_t off ) - { - wrt_request req( fd, buf, len, off ); - push( req ); - } - - void push( wrt_request &req ) - { - std::unique_lock lck( mtx ); - q.push( req ); - cv.notify_all(); - } - - wrt_request pop() - { - std::unique_lock lck( mtx ); - while( q.empty() && working ) - cv.wait( lck ); - if( !working ) return wrt_request(); - wrt_request req = q.front(); - q.pop(); - return req; - } - - void wait_done() - { - std::unique_lock lck( mtx2 ); - while( !q.empty() ) - { - cv2.wait( lck ); - } - std::cout << "done waiting" << std::endl; - } - - void done() - { - std::unique_lock lck( mtx2 ); - cv2.notify_all(); - } - - bool working; - std::thread worker; - std::queue q; - std::mutex mtx; - std::condition_variable cv; - - std::mutex mtx2; - std::condition_variable cv2; -}; - -void handle_error(const char *file, int lineno, const char *msg) { - fprintf(stderr, "** %s:%i %s\n", file, lineno, msg); - exit(-1); -} - -#define int_error(msg) handle_error(__FILE__, __LINE__, msg) - -void DoInitHS( SocketIO &io ) -{ - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.status = kXR_ok; - respHdr.dlen = htonl( 2 * sizeof( kXR_int32 ) ); - io.write( &respHdr, sizeof(ServerResponseHeader) ); - - ServerInitHandShake hs; - memset( &hs, 0, sizeof( ServerInitHandShake ) ); - hs.protover = htonl( 0x500 ); - io.write( &hs.protover, sizeof( kXR_int32 ) ); - hs.msgval = htonl( kXR_DataServer ); - io.write( &hs.msgval, sizeof( kXR_int32 ) ); -} - -void HandleProtocolReq( SocketIO &io, ClientRequestHdr *hdr ) -{ - std::stringstream ss; - ss << __func__ << std::endl; - - ClientProtocolRequest *req = (ClientProtocolRequest*)hdr; - - ss << "Client protocol version : " << std::hex << ntohl(req->clientpv) << std::dec << std::endl; - ss << "Flags : " << (int)req->flags << std::endl; - ss << "Expect : " << (int)req->expect << std::endl; - stdio.write( ss.str() ); - - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.status = kXR_ok; - respHdr.dlen = htonl( sizeof( ServerResponseBody_Protocol ) ); - io.write( &respHdr, sizeof(ServerResponseHeader) ); - - kXR_int32 flags = kXR_DataServer; - std::cout << "Server flags = " << flags << std::endl; - - ServerResponseBody_Protocol body; - body.pval = htonl( 0x500 ); - body.flags = htonl( flags ); - io.write( &body, sizeof(ServerResponseBody_Protocol) ); -} - -void HandleLoginReq( SocketIO &io, ClientRequestHdr *hdr ) -{ - ClientLoginRequest *req = (ClientLoginRequest*) hdr; - - std::stringstream ss; - ss << __func__ << std::endl; - ss << "Client PID : " << std::dec << ntohl( req->pid ) << std::endl; - - char *buffer = new char[hdr->dlen]; - io.read( buffer, hdr->dlen ); - ss << "Token : " << std::string( buffer, hdr->dlen ) << std::endl; - stdio.write( ss.str() ); - delete[] buffer; - - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.status = kXR_ok; - respHdr.dlen = htonl( 16 ); - io.write( &respHdr, sizeof( ServerResponseHeader ) ); - - ServerResponseBody_Login body; - memset( body.sessid, 0, 16 ); - io.write( &body, 16 ); -} - -int HandleOpenReq( SocketIO &io, ClientRequestHdr *hdr ) -{ - ClientOpenRequest *req = (ClientOpenRequest*) hdr; - std::stringstream ss; - ss << __func__ << std::endl; - ss << "Open mode : 0x" << std::hex << ntohs( req->mode ) << std::dec << std::endl; - - static const std::string statstr = "ABCD 1024 0 0"; - - char *buffer = new char[req->dlen]; - io.read( buffer, req->dlen ); - std::string path( buffer, req->dlen ); - io.set_fn( path ); - ss << "Path : " << std::string( buffer, req->dlen ) << std::endl; - delete[] buffer; - - ss << "opening : " << path << std::endl; - int fd = open( path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_DIRECT, 0664 ); - if( fd < 0 ) - stdio.write( strerror( errno ) ); - else - ss << "file opened : " << fd << std::endl; - - - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.streamid[0] = req->streamid[0]; - respHdr.streamid[1] = req->streamid[1]; - respHdr.status = kXR_ok; - respHdr.dlen = htonl( 12 + statstr.size() ); - io.write( &respHdr, sizeof( ServerResponseHeader ) ); - - ServerResponseBody_Open body; - memset( body.fhandle, 0, 4 ); - memset( body.cptype, 0, 4 ); - body.cpsize = 0; - io.write( &body, 12 ); - io.write( statstr.c_str(), statstr.size() ); - stdio.write( ss.str() ); - - return fd; -} - - void HandleStatReq( SocketIO &io, ClientRequestHdr *hdr ) -{ - ClientStatRequest *req = (ClientStatRequest*) hdr; - std::stringstream ss; - ss << __func__ << std::endl; - - static const std::string statstr = "ABCD 1024 0 0"; - - char *buffer = new char[req->dlen]; - io.read( buffer, req->dlen ); - std::string path( buffer, req->dlen ); - ss << "Path : " << std::string( buffer, req->dlen ) << std::endl; - delete[] buffer; - - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.streamid[0] = req->streamid[0]; - respHdr.streamid[1] = req->streamid[1]; - respHdr.status = kXR_ok; - respHdr.dlen = htonl( statstr.size() ); - io.write( &respHdr, sizeof( ServerResponseHeader ) ); - io.write( statstr.c_str(), statstr.size() ); - stdio.write( ss.str() ); -} - -void HandleWriteReq( SocketIO &io, ClientRequestHdr *hdr, int fd, wrt_queue &wq ) -{ - ClientWriteRequest *req = (ClientWriteRequest*) hdr; - std::stringstream ss; - ss << __func__ << " : " << "control stream." << std::endl; - ss << "req->dlen = " << req->dlen << std::endl; - req->offset = ntohll( req->offset ); - ss << std::dec << "Read " << req->dlen << " bytes from socket."; - void *ptr = nullptr; - posix_memalign( &ptr, 512, req->dlen ); - char *buffer = (char*)ptr; - io.read( buffer, req->dlen ); - wq.write( fd, buffer, req->dlen, req->offset ); - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.streamid[0] = req->streamid[0]; - respHdr.streamid[1] = req->streamid[1]; - respHdr.status = kXR_ok; - respHdr.dlen = 0; - - // pick up the I/O based on the pathid - io.write( &respHdr, sizeof( ServerResponseHeader ) ); - //stdio.write( ss.str() ); -} - -void HandleReadReq( SocketIO &io, ClientRequestHdr *hdr ) -{ - ClientReadRequest *req = (ClientReadRequest*) hdr; - std::stringstream ss; - ss << __func__ << " : " << "control stream." << std::endl; - - req->rlen = ntohl( req->rlen ); - req->offset = ntohll( req->offset ); - ss << std::dec << "Read " << req->rlen << " bytes at " << req->offset << " offset" << std::endl; - - static const std::string readstr = "ala ma kota, a ola ma psa, a ela ma rybke"; - - if( req->dlen ) - { - ss << "alen : " << req->dlen << std::endl; - char *buffer = new char[req->dlen]; - io.read( buffer, req->dlen ); - - read_args* rargs = (read_args*)buffer; - ss << "Path ID : " << (int)rargs->pathid << std::endl; - delete[] buffer; - } - - int dlen = req->rlen > int( readstr.size() ) ? readstr.size() : req->rlen; - - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.streamid[0] = req->streamid[0]; - respHdr.streamid[1] = req->streamid[1]; - respHdr.status = kXR_ok; - respHdr.dlen = htonl( dlen ); - - // pick up the I/O based on the pathid - io.write( &respHdr, sizeof( ServerResponseHeader ) ); - io.write( readstr.c_str(), dlen ); - stdio.write( ss.str() ); -} - -void HandleCloseReq( SocketIO &io, ClientRequestHdr *hdr, int fd, wrt_queue &wq ) -{ - ClientReadRequest *req = (ClientReadRequest*) hdr; - std::stringstream ss; - ss << __func__ << std::endl; - ss << "Closing: " << io.get_fn() << std::endl; - stdio.write( ss.str() ); - - wq.wait_done(); - int rc = close( fd ); - if( rc < 0 ) - stdio.write( strerror( errno ) ); - - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.streamid[0] = req->streamid[0]; - respHdr.streamid[1] = req->streamid[1]; - respHdr.status = kXR_ok; - respHdr.dlen = 0; - io.write( &respHdr, sizeof( ServerResponseHeader ) ); -} - - -int HandleRequest( SocketIO &io ) -{ - char buffer[1024] = {0}; - std::fill( buffer, buffer + 1024, 'A' ); - int valread = io.read( buffer, 20 ); - std::stringstream ss; - ss << "valread : " << valread << std::endl; - if( valread != 20 ) return -1; - ClientInitHandShake *init = (ClientInitHandShake*)buffer; - ss << "First : " << ntohl( init->first ) << std::endl; - ss << "Second : " << ntohl( init->second ) << std::endl; - ss << "Third : " << ntohl( init->third ) << std::endl; - ss << "Fourth : " << ntohl( init->fourth ) << std::endl; - ss << "Fifth : " << ntohl( init->fifth ) << std::endl; - stdio.write( ss.str() ); - - DoInitHS( io ); - - wrt_queue wrtq; - int fd = -1; - - while( true ) - { - std::stringstream ss; - ss << std::endl; - ss << "Waiting for client ..." << std::endl; - ss << "reading : " << sizeof( ClientRequestHdr ) << " bytes." << std::endl; - valread = io.read( buffer, sizeof( ClientRequestHdr ) ); - ss << "valread : " << valread << std::endl; - if( valread < 0 ) - { - return -1; - } - else if( valread == 0 ) - { - stdio.write( "client terminated the connection"); - return 0; - } - else if( valread < 8 ) - { - std::cout << "Got bogus header : " << valread << std::endl; - std::cout << std::string( buffer, valread ) << std::endl; - return -1; - } - - ClientRequestHdr *hdr = (ClientRequestHdr*)buffer; - hdr->dlen = ntohl( hdr->dlen ); - hdr->requestid = ntohs( hdr->requestid ); - - ss << "Got request: " << hdr->requestid << std::endl; - //stdio.write( ss.str() ); - - switch( hdr->requestid ) - { - case kXR_close: - { - stdio.write( "Got kXR_close!" ); - HandleCloseReq( io, hdr, fd, wrtq ); - fd = -1; - break; - } - - case kXR_protocol: - { - stdio.write( "Got kXR_protocol!" ); - HandleProtocolReq( io, hdr ); - break; - } - - case kXR_login: - { - stdio.write( "Got kXR_login!" ); - HandleLoginReq( io, hdr ); - break; - } - - case kXR_open: - { - stdio.write( "Got kXR_open!" ); - fd = HandleOpenReq( io, hdr ); - break; - } - - case kXR_read: - { - stdio.write( "Got kXR_read!" ); - HandleReadReq( io, hdr ); - break; - } - - case kXR_stat: - { - stdio.write( "Got kXR_stat!" ); - HandleStatReq( io, hdr ); - break; - } - - case kXR_write: - { - //stdio.write( "Got kXR_write!" ); - HandleWriteReq( io, hdr, fd, wrtq ); - break; - } - - default: - { - stdio.write( "Got unsupported request!" ); - break; - } - }; - } - - return 0; -} - - -void control_stream( int socket ) -{ - std::stringstream ss; - ss << '\n' << __func__ << '\n'; - stdio.write( ss.str() ); - SocketIO io( socket ); - HandleRequest( io ); -} - - -int main(int argc, char const *argv[]) -{ - int server_fd, new_socket; - struct sockaddr_in address; - int opt = 1; - int addrlen = sizeof(address); - - // Creating socket file descriptor - if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) - { - perror("socket failed"); - exit(EXIT_FAILURE); - } - - // Forcefully attaching socket to the port 8080 - if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, - &opt, sizeof(opt))) - { - perror("setsockopt"); - exit(EXIT_FAILURE); - } - address.sin_family = AF_INET; - address.sin_addr.s_addr = INADDR_ANY; - address.sin_port = htons( 2094 ); - - // Forcefully attaching socket to the port 8080 - if (bind(server_fd, (struct sockaddr *)&address, - sizeof(address))<0) - { - perror("bind failed"); - exit(EXIT_FAILURE); - } - if (listen(server_fd, 3) < 0) - { - perror("listen"); - exit(EXIT_FAILURE); - } - - std::list threads; - - while( true ) - { - std::stringstream ss; - ss << "Waiting to accept new TCP connection!" << std::endl; - stdio.write( ss.str() ); - if ((new_socket = accept(server_fd, (struct sockaddr *)&address, // TODO - (socklen_t*)&addrlen))<0) - { - perror("accept"); - exit(EXIT_FAILURE); - } - ss << "New TCP connection accepted!" << std::endl; - stdio.write( ss.str() ); - - threads.emplace_back( control_stream, new_socket ); - } - - std::cout << "The End." << std::endl; - - return 0; -} diff --git a/tests/XrdClTests/wrt/xrdsrv-wrt.cc b/tests/XrdClTests/wrt/xrdsrv-wrt.cc deleted file mode 100644 index 7eeb5388aac..00000000000 --- a/tests/XrdClTests/wrt/xrdsrv-wrt.cc +++ /dev/null @@ -1,563 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - - -#include "XrdCl/XrdClXRootDTransport.hh" -#include "XProtocol/XProtocol.hh" -#include "XrdSys/XrdSysPlatform.hh" -#include "XrdTls/XrdTlsContext.hh" - -struct StdIO -{ - void write( const std::string &str ) - { - std::unique_lock lck( mtx ); - std::cout << str << std::endl; - } - - private: - - std::mutex mtx; -} stdio; - -struct SocketIO -{ - SocketIO( int socket ) : socket( socket ) - { - } - - ~SocketIO() - { - } - - int read( void *buffer, int size ) - { - int ret = 0; - - char *buff = static_cast( buffer ); - while ( size != 0 ) - { - int rc = ::read( socket, buff, size ); - - if( rc <= 0 ) - break; - - ret += rc; - buff += rc; - size -= rc; - } - - return ret; - } - - int write( const void *buffer, int size ) - { - int rc = ::write( socket, buffer, size ); - return rc; - } - - private: - int socket; - -}; - -struct wrt_queue -{ - wrt_queue() : working( true ), worker( work, this ) - { - } - - ~wrt_queue() - { - working = false; - cv.notify_all(); - worker.join(); - } - - static void work( wrt_queue *myself ) - { - while( myself->working ) - { - wrt_request req = myself->pop(); - if( !myself->working ) return; - int rc = pwrite( req.fd, req.buf, req.len, req.off ); - if( rc < 0 ) - stdio.write( strerror( errno ) ); - delete[] req.buf; - } - } - - struct wrt_request - { - wrt_request( int fd, const char *buf, size_t len, off_t off ) : - fd( fd ), buf( buf ), len( len ), off( off ) - { - } - - wrt_request() : fd( -1 ), buf( nullptr ), len( 0 ), off( 0 ){ } - - int fd; - const char *buf; - size_t len; - off_t off; - }; - - void write( int fd, const char *buf, size_t len, off_t off ) - { - wrt_request req( fd, buf, len, off ); - push( req ); - } - - void push( wrt_request &req ) - { - std::unique_lock lck( mtx ); - q.push( req ); - cv.notify_all(); - } - - wrt_request pop() - { - std::unique_lock lck( mtx ); - while( q.empty() && working ) - cv.wait( lck ); - if( !working ) return wrt_request(); - wrt_request req = q.front(); - q.pop(); - return req; - } - - bool working; - std::thread worker; - std::queue q; - std::mutex mtx; - std::condition_variable cv; -}; - -void handle_error(const char *file, int lineno, const char *msg) { - fprintf(stderr, "** %s:%i %s\n", file, lineno, msg); - exit(-1); -} - -#define int_error(msg) handle_error(__FILE__, __LINE__, msg) - -void DoInitHS( SocketIO &io ) -{ - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.status = kXR_ok; - respHdr.dlen = htonl( 2 * sizeof( kXR_int32 ) ); - io.write( &respHdr, sizeof(ServerResponseHeader) ); - - ServerInitHandShake hs; - memset( &hs, 0, sizeof( ServerInitHandShake ) ); - hs.protover = htonl( 0x500 ); - io.write( &hs.protover, sizeof( kXR_int32 ) ); - hs.msgval = htonl( kXR_DataServer ); - io.write( &hs.msgval, sizeof( kXR_int32 ) ); -} - -void HandleProtocolReq( SocketIO &io, ClientRequestHdr *hdr ) -{ - std::stringstream ss; - ss << __func__ << std::endl; - - ClientProtocolRequest *req = (ClientProtocolRequest*)hdr; - - ss << "Client protocol version : " << std::hex << ntohl(req->clientpv) << std::dec << std::endl; - ss << "Flags : " << (int)req->flags << std::endl; - ss << "Expect : " << (int)req->expect << std::endl; - stdio.write( ss.str() ); - - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.status = kXR_ok; - respHdr.dlen = htonl( sizeof( ServerResponseBody_Protocol ) ); - io.write( &respHdr, sizeof(ServerResponseHeader) ); - - kXR_int32 flags = kXR_DataServer; - std::cout << "Server flags = " << flags << std::endl; - - ServerResponseBody_Protocol body; - body.pval = htonl( 0x500 ); - body.flags = htonl( flags ); - io.write( &body, sizeof(ServerResponseBody_Protocol) ); -} - -void HandleLoginReq( SocketIO &io, ClientRequestHdr *hdr ) -{ - ClientLoginRequest *req = (ClientLoginRequest*) hdr; - - std::stringstream ss; - ss << __func__ << std::endl; - ss << "Client PID : " << std::dec << ntohl( req->pid ) << std::endl; - - char *buffer = new char[hdr->dlen]; - io.read( buffer, hdr->dlen ); - ss << "Token : " << std::string( buffer, hdr->dlen ) << std::endl; - stdio.write( ss.str() ); - delete[] buffer; - - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.status = kXR_ok; - respHdr.dlen = htonl( 16 ); - io.write( &respHdr, sizeof( ServerResponseHeader ) ); - - ServerResponseBody_Login body; - memset( body.sessid, 0, 16 ); - io.write( &body, 16 ); -} - -int HandleOpenReq( SocketIO &io, ClientRequestHdr *hdr ) -{ - ClientOpenRequest *req = (ClientOpenRequest*) hdr; - std::stringstream ss; - ss << __func__ << std::endl; - ss << "Open mode : 0x" << std::hex << ntohs( req->mode ) << std::dec << std::endl; - - static const std::string statstr = "ABCD 1024 0 0"; - - char *buffer = new char[req->dlen]; - io.read( buffer, req->dlen ); - std::string path( buffer, req->dlen ); - ss << "Path : " << std::string( buffer, req->dlen ) << std::endl; - delete[] buffer; - - ss << "opening : " << path << std::endl; - int fd = open( path.c_str(), O_WRONLY | O_CREAT, 0664 ); - if( fd < 0 ) - stdio.write( strerror( errno ) ); - else - ss << "file opened : " << fd << std::endl; - - - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.streamid[0] = req->streamid[0]; - respHdr.streamid[1] = req->streamid[1]; - respHdr.status = kXR_ok; - respHdr.dlen = htonl( 12 + statstr.size() ); - io.write( &respHdr, sizeof( ServerResponseHeader ) ); - - ServerResponseBody_Open body; - memset( body.fhandle, 0, 4 ); - memset( body.cptype, 0, 4 ); - body.cpsize = 0; - io.write( &body, 12 ); - io.write( statstr.c_str(), statstr.size() ); - stdio.write( ss.str() ); - - return fd; -} - - void HandleStatReq( SocketIO &io, ClientRequestHdr *hdr ) -{ - ClientStatRequest *req = (ClientStatRequest*) hdr; - std::stringstream ss; - ss << __func__ << std::endl; - - static const std::string statstr = "ABCD 1024 0 0"; - - char *buffer = new char[req->dlen]; - io.read( buffer, req->dlen ); - std::string path( buffer, req->dlen ); - ss << "Path : " << std::string( buffer, req->dlen ) << std::endl; - delete[] buffer; - - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.streamid[0] = req->streamid[0]; - respHdr.streamid[1] = req->streamid[1]; - respHdr.status = kXR_ok; - respHdr.dlen = htonl( statstr.size() ); - io.write( &respHdr, sizeof( ServerResponseHeader ) ); - io.write( statstr.c_str(), statstr.size() ); - stdio.write( ss.str() ); -} - -void HandleWriteReq( SocketIO &io, ClientRequestHdr *hdr, int fd, wrt_queue &wq ) -{ - ClientWriteRequest *req = (ClientWriteRequest*) hdr; - std::stringstream ss; - ss << __func__ << " : " << "control stream." << std::endl; - ss << "req->dlen = " << req->dlen << std::endl; - req->offset = ntohll( req->offset ); - ss << std::dec << "Read " << req->dlen << " bytes from socket."; - char *buffer = new char[req->dlen]; - io.read( buffer, req->dlen ); - wq.write( fd, buffer, req->dlen, req->offset ); - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.streamid[0] = req->streamid[0]; - respHdr.streamid[1] = req->streamid[1]; - respHdr.status = kXR_ok; - respHdr.dlen = 0; - - // pick up the I/O based on the pathid - io.write( &respHdr, sizeof( ServerResponseHeader ) ); - stdio.write( ss.str() ); -} - -void HandleReadReq( SocketIO &io, ClientRequestHdr *hdr ) -{ - ClientReadRequest *req = (ClientReadRequest*) hdr; - std::stringstream ss; - ss << __func__ << " : " << "control stream." << std::endl; - - req->rlen = ntohl( req->rlen ); - req->offset = ntohll( req->offset ); - ss << std::dec << "Read " << req->rlen << " bytes at " << req->offset << " offset" << std::endl; - - static const std::string readstr = "ala ma kota, a ola ma psa, a ela ma rybke"; - - kXR_char pathid = 0; - if( req->dlen ) - { - ss << "alen : " << req->dlen << std::endl; - char *buffer = new char[req->dlen]; - io.read( buffer, req->dlen ); - - read_args* rargs = (read_args*)buffer; - pathid = rargs->pathid; - ss << "Path ID : " << (int)rargs->pathid << std::endl; - delete[] buffer; - } - - int dlen = req->rlen > int( readstr.size() ) ? readstr.size() : req->rlen; - - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.streamid[0] = req->streamid[0]; - respHdr.streamid[1] = req->streamid[1]; - respHdr.status = kXR_ok; - respHdr.dlen = htonl( dlen ); - - // pick up the I/O based on the pathid - io.write( &respHdr, sizeof( ServerResponseHeader ) ); - io.write( readstr.c_str(), dlen ); - stdio.write( ss.str() ); -} - -void HandleCloseReq( SocketIO &io, ClientRequestHdr *hdr, int fd ) -{ - ClientReadRequest *req = (ClientReadRequest*) hdr; - std::stringstream ss; - ss << __func__ << std::endl; - stdio.write( ss.str() ); - - int rc = close( fd ); - if( rc < 0 ) - stdio.write( strerror( errno ) ); - - ServerResponseHeader respHdr; - memset( &respHdr, 0, sizeof( ServerResponseHeader ) ); - respHdr.streamid[0] = req->streamid[0]; - respHdr.streamid[1] = req->streamid[1]; - respHdr.status = kXR_ok; - respHdr.dlen = 0; - io.write( &respHdr, sizeof( ServerResponseHeader ) ); -} - - -int HandleRequest( SocketIO &io ) -{ - char buffer[1024] = {0}; - std::fill( buffer, buffer + 1024, 'A' ); - int valread = io.read( buffer, 20 ); - std::stringstream ss; - ss << "valread : " << valread << std::endl; - if( valread != 20 ) return -1; - ClientInitHandShake *init = (ClientInitHandShake*)buffer; - ss << "First : " << ntohl( init->first ) << std::endl; - ss << "Second : " << ntohl( init->second ) << std::endl; - ss << "Third : " << ntohl( init->third ) << std::endl; - ss << "Fourth : " << ntohl( init->fourth ) << std::endl; - ss << "Fifth : " << ntohl( init->fifth ) << std::endl; - stdio.write( ss.str() ); - - DoInitHS( io ); - - wrt_queue wrtq; - int fd = -1; - - while( true ) - { - std::stringstream ss; - ss << std::endl; - ss << "Waiting for client ..." << std::endl; - ss << "reading : " << sizeof( ClientRequestHdr ) << " bytes." << std::endl; - valread = io.read( buffer, sizeof( ClientRequestHdr ) ); - ss << "valread : " << valread << std::endl; - if( valread < 0 ) - { - return -1; - } - else if( valread == 0 ) - { - stdio.write( "client terminated the connection"); - return 0; - } - else if( valread < 8 ) - { - std::cout << "Got bogus header : " << valread << std::endl; - std::cout << std::string( buffer, valread ) << std::endl; - return -1; - } - - ClientRequestHdr *hdr = (ClientRequestHdr*)buffer; - hdr->dlen = ntohl( hdr->dlen ); - hdr->requestid = ntohs( hdr->requestid ); - - ss << "Got request: " << hdr->requestid << std::endl; - stdio.write( ss.str() ); - - switch( hdr->requestid ) - { - case kXR_close: - { - stdio.write( "Got kXR_close!" ); - HandleCloseReq( io, hdr, fd ); - fd = -1; - break; - } - - case kXR_protocol: - { - stdio.write( "Got kXR_protocol!" ); - HandleProtocolReq( io, hdr ); - break; - } - - case kXR_login: - { - stdio.write( "Got kXR_login!" ); - HandleLoginReq( io, hdr ); - break; - } - - case kXR_open: - { - stdio.write( "Got kXR_open!" ); - fd = HandleOpenReq( io, hdr ); - break; - } - - case kXR_read: - { - stdio.write( "Got kXR_read!" ); - HandleReadReq( io, hdr ); - break; - } - - case kXR_stat: - { - stdio.write( "Got kXR_stat!" ); - HandleStatReq( io, hdr ); - break; - } - - case kXR_write: - { - stdio.write( "Got kXR_write!" ); - HandleWriteReq( io, hdr, fd, wrtq ); - break; - } - - default: - { - stdio.write( "Got unsupported request!" ); - break; - } - }; - } - - return 0; -} - - -void control_stream( int socket ) -{ - std::stringstream ss; - ss << '\n' << __func__ << '\n'; - stdio.write( ss.str() ); - SocketIO io( socket ); - HandleRequest( io ); -} - - -int main(int argc, char const *argv[]) -{ - int server_fd, new_socket; - struct sockaddr_in address; - int opt = 1; - int addrlen = sizeof(address); - - // Creating socket file descriptor - if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) - { - perror("socket failed"); - exit(EXIT_FAILURE); - } - - // Forcefully attaching socket to the port 8080 - if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, - &opt, sizeof(opt))) - { - perror("setsockopt"); - exit(EXIT_FAILURE); - } - address.sin_family = AF_INET; - address.sin_addr.s_addr = INADDR_ANY; - address.sin_port = htons( 1094 ); - - // Forcefully attaching socket to the port 8080 - if (bind(server_fd, (struct sockaddr *)&address, - sizeof(address))<0) - { - perror("bind failed"); - exit(EXIT_FAILURE); - } - if (listen(server_fd, 3) < 0) - { - perror("listen"); - exit(EXIT_FAILURE); - } - - std::list threads; - - while( true ) - { - std::stringstream ss; - ss << "Waiting to accept new TCP connection!" << std::endl; - stdio.write( ss.str() ); - if ((new_socket = accept(server_fd, (struct sockaddr *)&address, // TODO - (socklen_t*)&addrlen))<0) - { - perror("accept"); - exit(EXIT_FAILURE); - } - ss << "New TCP connection accepted!" << std::endl; - stdio.write( ss.str() ); - - threads.emplace_back( control_stream, new_socket ); - } - - std::cout << "The End." << std::endl; - - return 0; -} From 751a2dec88e9047cb44b41136938be305e578394 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Thu, 8 Feb 2024 16:06:03 +0100 Subject: [PATCH 017/276] [Tests] Remove CppUnit code from common tests Convert also the common test code into shared library and link tests against it instead of recompiling the code into each binary. --- tests/CMakeLists.txt | 3 - tests/XrdCl/CMakeLists.txt | 51 +------ tests/XrdCl/GTestXrdHelpers.hh | 24 +-- tests/XrdEc/CMakeLists.txt | 29 +--- tests/XrdEc/{MicroTest.cc => XrdEcTests.cc} | 0 tests/common/CMakeLists.txt | 44 +----- tests/common/CppUnitXrdHelpers.hh | 57 -------- tests/common/PathProcessor.hh | 83 ----------- tests/common/TextRunner.cc | 153 -------------------- 9 files changed, 22 insertions(+), 422 deletions(-) rename tests/XrdEc/{MicroTest.cc => XrdEcTests.cc} (100%) delete mode 100644 tests/common/CppUnitXrdHelpers.hh delete mode 100644 tests/common/PathProcessor.hh delete mode 100644 tests/common/TextRunner.cc diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 84f3a653f12..bb88cc32e26 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,10 +8,7 @@ add_subdirectory(common) add_subdirectory(XrdCl) add_subdirectory(XrdCeph) - -if(BUILD_XRDEC) add_subdirectory(XrdEc) -endif() add_subdirectory(XrdHttpTests) diff --git a/tests/XrdCl/CMakeLists.txt b/tests/XrdCl/CMakeLists.txt index 015f4c92501..6e06487c550 100644 --- a/tests/XrdCl/CMakeLists.txt +++ b/tests/XrdCl/CMakeLists.txt @@ -3,23 +3,10 @@ add_executable(xrdcl-unit-tests XrdClPoller.cc XrdClSocket.cc XrdClUtilsTest.cc - ../common/Server.cc - ../common/Utils.cc - ../common/TestEnv.cc ) target_link_libraries(xrdcl-unit-tests - XrdCl - XrdXml - XrdUtils - ZLIB::ZLIB - GTest::GTest - GTest::Main -) - -target_include_directories(xrdcl-unit-tests - PRIVATE ${CMAKE_SOURCE_DIR}/src ../common -) + XrdTestUtils GTest::GTest GTest::Main) gtest_discover_tests(xrdcl-unit-tests TEST_PREFIX XrdCl::) @@ -45,23 +32,10 @@ add_executable(xrdcl-cluster-tests XrdClPostMasterTest.cc XrdClThreadingTest.cc XrdClZip.cc - ../common/Server.cc - ../common/Utils.cc - ../common/TestEnv.cc ) target_link_libraries(xrdcl-cluster-tests - XrdCl - XrdXml - XrdUtils - ZLIB::ZLIB - GTest::GTest - GTest::Main -) - -target_include_directories(xrdcl-cluster-tests - PRIVATE ${CMAKE_SOURCE_DIR}/src ../common -) + XrdTestUtils GTest::GTest GTest::Main) gtest_discover_tests(xrdcl-cluster-tests TEST_PREFIX XrdCl:: PROPERTIES DEPENDS XRootD_Cluster FIXTURES_REQUIRED XRootD_Cluster) @@ -76,28 +50,11 @@ set(SERIAL_TESTS_FILES # create a separate executable target for each "problematic" test suite foreach(i ${SERIAL_TESTS_FILES}) - add_executable(${i}-tests - ${i} - IdentityPlugIn.cc - ../common/Server.cc - ../common/Utils.cc - ../common/TestEnv.cc - ) + add_executable(${i}-tests ${i} IdentityPlugIn.cc) target_link_libraries(${i}-tests - XrdCl - XrdXml - XrdUtils - ZLIB::ZLIB - GTest::GTest - GTest::Main - ) - - target_include_directories(${i}-tests - PRIVATE ${CMAKE_SOURCE_DIR}/src ../common - ) + XrdTestUtils GTest::GTest GTest::Main) gtest_discover_tests(${i}-tests TEST_PREFIX XrdCl:: PROPERTIES DEPENDS XRootD_Cluster FIXTURES_REQUIRED XRootD_Cluster RUN_SERIAL 1) - endforeach() diff --git a/tests/XrdCl/GTestXrdHelpers.hh b/tests/XrdCl/GTestXrdHelpers.hh index d54ef29c95a..8569289323e 100644 --- a/tests/XrdCl/GTestXrdHelpers.hh +++ b/tests/XrdCl/GTestXrdHelpers.hh @@ -24,43 +24,27 @@ #include #include -/** @brief Equivalent of CPPUNIT_ASSERT_XRDST - * - * Shows the code that we are asserting and its value - * in the final evaluation. - */ +/** Shows the code that we are asserting and its value in the final evaluation. */ #define GTEST_ASSERT_XRDST( x ) \ { \ XrdCl::XRootDStatus _st = x; \ EXPECT_TRUE(_st.IsOK()) << "[" << #x << "]: " << _st.ToStr() << std::endl; \ } -/** @brief Equivalent of CPPUNIT_ASSERT_XRDST_NOTOK - * - * Shows the code that we are asserting and asserts that its - * execution is throwing an error. - */ +/** Shows the code that we are asserting and asserts that its execution is throwing an error. */ #define GTEST_ASSERT_XRDST_NOTOK( x, err ) \ { \ XrdCl::XRootDStatus _st = x; \ EXPECT_TRUE(!_st.IsOK() && _st.code == err) << "[" << #x << "]: " << _st.ToStr() << std::endl; \ } -/** @brief Equivalent of CPPUNIT_ASSERT_ERRNO - * - * Shows the code that we are asserting and its error - * number. - */ +/** Shows the code that we are asserting and its error number. */ #define GTEST_ASSERT_ERRNO( x ) \ { \ EXPECT_TRUE(x) << "[" << #x << "]: " << strerror(errno) << std::endl; \ } -/** @brief Equivalent of GTEST_ASSERT_PTHREAD - * - * Shows the code that we are asserting and its error - * number, in a thread-safe manner. - */ +/** Shows the code that we are asserting and its error number, in a thread-safe manner. */ #define GTEST_ASSERT_PTHREAD( x ) \ { \ errno = x; \ diff --git a/tests/XrdEc/CMakeLists.txt b/tests/XrdEc/CMakeLists.txt index 0531c35ec43..f8052b4e0cd 100644 --- a/tests/XrdEc/CMakeLists.txt +++ b/tests/XrdEc/CMakeLists.txt @@ -1,25 +1,12 @@ -add_executable(xrdec-unit-tests - MicroTest.cc - ../common/Server.cc - ../common/Utils.cc - ../common/TestEnv.cc -) +if(NOT BUILD_XRDEC) + return() +endif() + +add_executable(xrdec-unit-tests XrdEcTests.cc) target_link_libraries(xrdec-unit-tests - XrdEc - XrdCl - XrdXml - XrdUtils - ZLIB::ZLIB - GTest::GTest - GTest::Main - ${ISAL_LIBRARIES} -) + XrdEc XrdTestUtils GTest::GTest GTest::Main ${ISAL_LIBRARIES}) -target_include_directories(xrdec-unit-tests - PRIVATE ${CMAKE_SOURCE_DIR}/src - PRIVATE ../common - ${ISAL_INCLUDE_DIRS} -) +target_include_directories(xrdec-unit-tests PRIVATE ${ISAL_INCLUDE_DIRS}) -gtest_discover_tests(xrdec-unit-tests TEST_PREFIX XrdCl::) +gtest_discover_tests(xrdec-unit-tests TEST_PREFIX XrdEc::) diff --git a/tests/XrdEc/MicroTest.cc b/tests/XrdEc/XrdEcTests.cc similarity index 100% rename from tests/XrdEc/MicroTest.cc rename to tests/XrdEc/XrdEcTests.cc diff --git a/tests/common/CMakeLists.txt b/tests/common/CMakeLists.txt index 5497a7116dc..d9501f315e9 100644 --- a/tests/common/CMakeLists.txt +++ b/tests/common/CMakeLists.txt @@ -1,41 +1,9 @@ - - -add_library( - XrdClTestsHelper SHARED - Server.cc Server.hh - Utils.cc Utils.hh +add_library(XrdTestUtils SHARED + Server.cc Server.hh TestEnv.cc TestEnv.hh - CppUnitXrdHelpers.hh) - -target_link_libraries( - XrdClTestsHelper - ${CMAKE_THREAD_LIBS_INIT} - ${CPPUNIT_LIBRARIES} - ZLIB::ZLIB - XrdCl - XrdUtils) - -target_include_directories( - XrdClTestsHelper PUBLIC ${CPPUNIT_INCLUDE_DIRS}) - -add_executable( - test-runner - TextRunner.cc - PathProcessor.hh) - -target_link_libraries( - test-runner - ${CMAKE_DL_LIBS} - ${CPPUNIT_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT}) + Utils.cc Utils.hh) -target_include_directories( - test-runner PRIVATE ${CPPUNIT_INCLUDE_DIRS}) +target_link_libraries(XrdTestUtils ZLIB::ZLIB XrdCl XrdUtils) -#------------------------------------------------------------------------------- -# Install -#------------------------------------------------------------------------------- -install( - TARGETS XrdClTestsHelper test-runner - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) +target_include_directories(XrdTestUtils + PUBLIC $) diff --git a/tests/common/CppUnitXrdHelpers.hh b/tests/common/CppUnitXrdHelpers.hh deleted file mode 100644 index b4634433133..00000000000 --- a/tests/common/CppUnitXrdHelpers.hh +++ /dev/null @@ -1,57 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2011-2012 by European Organization for Nuclear Research (CERN) -// Author: Lukasz Janyst -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -//------------------------------------------------------------------------------ - -#ifndef __CPPUNIT_XRD_HELPERS_HH__ -#define __CPPUNIT_XRD_HELPERS_HH__ - -#include -#include -#include - -#define CPPUNIT_ASSERT_XRDST_NOTOK( x, err ) \ -{ \ - XrdCl::XRootDStatus _st = x; \ - std::string msg = "["; msg += #x; msg += "]: "; \ - msg += _st.ToStr(); \ - CPPUNIT_ASSERT_MESSAGE( msg, !_st.IsOK() && _st.code == err ); \ -} - -#define CPPUNIT_ASSERT_XRDST( x ) \ -{ \ - XrdCl::XRootDStatus _st = x; \ - std::string msg = "["; msg += #x; msg += "]: "; \ - msg += _st.ToStr(); \ - CPPUNIT_ASSERT_MESSAGE( msg, _st.IsOK() ); \ -} - -#define CPPUNIT_ASSERT_ERRNO( x ) \ -{ \ - std::string msg = "["; msg += #x; msg += "]: "; \ - msg += strerror( errno ); \ - CPPUNIT_ASSERT_MESSAGE( msg, x ); \ -} - -#define CPPUNIT_ASSERT_PTHREAD( x ) \ -{ \ - errno = x; \ - std::string msg = "["; msg += #x; msg += "]: "; \ - msg += strerror( errno ); \ - CPPUNIT_ASSERT_MESSAGE( msg, errno == 0 ); \ -} - -#endif // __CPPUNIT_XRD_HELPERS_HH__ diff --git a/tests/common/PathProcessor.hh b/tests/common/PathProcessor.hh deleted file mode 100644 index 9d294cb3048..00000000000 --- a/tests/common/PathProcessor.hh +++ /dev/null @@ -1,83 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2011-2012 by European Organization for Nuclear Research (CERN) -// Author: Lukasz Janyst -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -//------------------------------------------------------------------------------ - -#ifndef EOS_NS_PATH_PROCESSOR_HH -#define EOS_NS_PATH_PROCESSOR_HH - -#include -#include -#include - -namespace eos -{ - //---------------------------------------------------------------------------- - //! Helper class responsible for spliting the path - //---------------------------------------------------------------------------- - class PathProcessor - { - public: - //------------------------------------------------------------------------ - //! Split the path and put its elements in a vector, the tokens are - //! copied, the buffer is not overwritten - //------------------------------------------------------------------------ - static void splitPath( std::vector &elements, - const std::string &path ) - { - elements.clear(); - std::vector elems; - char buffer[path.length()+1]; - strcpy( buffer, path.c_str() ); - splitPath( elems, buffer ); - for( size_t i = 0; i < elems.size(); ++i ) - elements.push_back( elems[i] ); - } - - //------------------------------------------------------------------------ - //! Split the path and put its element in a vector, the split is done - //! in-place and the buffer is overwritten - //------------------------------------------------------------------------ - static void splitPath( std::vector &elements, char *buffer ) - { - elements.clear(); - elements.reserve( 10 ); - - char *cursor = buffer; - char *beg = buffer; - - //---------------------------------------------------------------------- - // Go by the characters one by one - //---------------------------------------------------------------------- - while( *cursor ) - { - if( *cursor == '/' ) - { - *cursor = 0; - if( beg != cursor ) - elements.push_back( beg ); - beg = cursor+1; - } - ++cursor; - } - - if( beg != cursor ) - elements.push_back( beg ); - } - }; -} - -#endif // EOS_NS_PATH_PROCESSOR_HH diff --git a/tests/common/TextRunner.cc b/tests/common/TextRunner.cc deleted file mode 100644 index 26f5ff86514..00000000000 --- a/tests/common/TextRunner.cc +++ /dev/null @@ -1,153 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2011-2012 by European Organization for Nuclear Research (CERN) -// Author: Lukasz Janyst -//------------------------------------------------------------------------------ -// 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 General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with XRootD. If not, see . -//------------------------------------------------------------------------------ - -#include -#include -#include -#include -#include "PathProcessor.hh" - -//------------------------------------------------------------------------------ -// Print all the tests present in the test suite -//------------------------------------------------------------------------------ -void printTests( const CppUnit::Test *t, std::string prefix = "" ) -{ - if( t == 0 ) - return; - - const CppUnit::TestSuite *suite = dynamic_cast( t ); - std::cerr << prefix << t->getName(); - if( suite ) - { - std::cerr << "/" << std::endl; - std::string prefix1 = " "; prefix1 += prefix; - prefix1 += t->getName(); prefix1 += "/"; - const std::vector &tests = suite->getTests(); - std::vector::const_iterator it; - for( it = tests.begin(); it != tests.end(); ++it ) - printTests( *it, prefix1 ); - } - else - std::cerr << std::endl; -} - -//------------------------------------------------------------------------------ -// Find a test -//------------------------------------------------------------------------------ -CppUnit::Test *findTest( CppUnit::Test *t, const std::string &test ) -{ - //---------------------------------------------------------------------------- - // Check the suit and the path - //---------------------------------------------------------------------------- - std::vector elements; - eos::PathProcessor::splitPath( elements, test ); - - if( t == 0 ) - return 0; - - if( elements.empty() ) - return 0; - - if( t->getName() != elements[0] ) - return 0; - - //---------------------------------------------------------------------------- - // Look for the requested test - //---------------------------------------------------------------------------- - CppUnit::Test *ret = t; - for( size_t i = 1; i < elements.size(); ++i ) - { - CppUnit::TestSuite *suite = dynamic_cast( ret ); - CppUnit::Test *next = 0; - const std::vector &tests = suite->getTests(); - std::vector::const_iterator it; - for( it = tests.begin(); it != tests.end(); ++it ) - if( (*it)->getName() == elements[i] ) - next = *it; - if( !next ) - return 0; - ret = next; - } - - return ret; -} - -//------------------------------------------------------------------------------ -// Start the show -//------------------------------------------------------------------------------ -int main( int argc, char **argv) -{ - //---------------------------------------------------------------------------- - // Load the test library - //---------------------------------------------------------------------------- - if( argc < 2 ) - { - std::cerr << "Usage: " << argv[0] << " libname.so testname" << std::endl; - return 1; - } - void *libHandle = dlopen( argv[1], RTLD_LAZY ); - if( libHandle == 0 ) - { - std::cerr << "Unable to load the test library: " << dlerror() << std::endl; - return 1; - } - - //---------------------------------------------------------------------------- - // Print help - //---------------------------------------------------------------------------- - CppUnit::Test *all = CppUnit::TestFactoryRegistry::getRegistry().makeTest(); - if( argc == 2 ) - { - std::cerr << "Select your tests:" << std::endl << std::endl; - printTests( all ); - std::cerr << std::endl; - return 1; - } - - //---------------------------------------------------------------------------- - // Build the test suite - //---------------------------------------------------------------------------- - CppUnit::TestSuite *selected = new CppUnit::TestSuite( "Selected tests" ); - for( int i = 2; i < argc; ++i ) - { - CppUnit::Test *t = findTest( all, std::string( argv[i]) ); - if( !t ) - { - std::cerr << "Unable to find: " << argv[i] << std::endl; - return 2; - } - selected->addTest( t ); - } - - std::cerr << "You have selected: " << std::endl << std::endl; - printTests( selected ); - std::cerr << std::endl; - - //---------------------------------------------------------------------------- - // Run the tests - //---------------------------------------------------------------------------- - std::cerr << "Running:" << std::endl << std::endl; - CppUnit::TextUi::TestRunner runner; - runner.addTest( selected ); - - runner.setOutputter( - new CppUnit::CompilerOutputter( &runner.result(), std::cerr ) ); - - bool wasSuccessful = runner.run(); - return wasSuccessful ? 0 : 1; -} From b34a4eb78ff9f2d8b772a9c2908b2c2969f062f8 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Thu, 8 Feb 2024 16:15:21 +0100 Subject: [PATCH 018/276] [CMake,DEB,RPM] Remove CppUnit code from build system Fixes: #2051 --- cmake/FindCppUnit.cmake | 30 ------------------------------ cmake/XRootDFindLibs.cmake | 4 +--- cmake/XRootDSummary.cmake | 2 +- debian/control | 1 - xrootd.spec | 6 ------ 5 files changed, 2 insertions(+), 41 deletions(-) delete mode 100644 cmake/FindCppUnit.cmake diff --git a/cmake/FindCppUnit.cmake b/cmake/FindCppUnit.cmake deleted file mode 100644 index cc6e7400191..00000000000 --- a/cmake/FindCppUnit.cmake +++ /dev/null @@ -1,30 +0,0 @@ -# Try to find CPPUnit -# Once done, this will define -# -# CPPUNIT_FOUND - system has cppunit -# CPPUNIT_INCLUDE_DIRS - the cppunit include directories -# CPPUNIT_LIBRARIES - cppunit libraries directories - -find_path( CPPUNIT_INCLUDE_DIRS cppunit/ui/text/TestRunner.h - HINTS - ${CPPUNIT_DIR} - $ENV{CPPUNIT_DIR} - /usr - /opt - PATH_SUFFIXES include -) - -find_library( CPPUNIT_LIBRARIES cppunit - HINTS - ${CPPUNIT_DIR} - $ENV{CPPUNIT_DIR} - /usr - /opt - PATH_SUFFIXES lib -) - -set(CPPUNIT_INCLUDE_DIRS ${CPPUNIT_INCLUDE_DIR}) -set(CPPUNIT_LIBRARIES ${CPPUNIT_LIBRARY}) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(CppUnit DEFAULT_MSG CPPUNIT_INCLUDE_DIRS CPPUNIT_LIBRARIES) diff --git a/cmake/XRootDFindLibs.cmake b/cmake/XRootDFindLibs.cmake index 30775cca90f..435d07dbbbd 100644 --- a/cmake/XRootDFindLibs.cmake +++ b/cmake/XRootDFindLibs.cmake @@ -72,13 +72,11 @@ endif() if( ENABLE_TESTS ) if( FORCE_ENABLED ) - find_package( CppUnit REQUIRED ) find_package( GTest REQUIRED ) else() - find_package( CppUnit ) find_package( GTest ) endif() - if( CPPUNIT_FOUND AND GTEST_FOUND ) + if( GTEST_FOUND ) set( BUILD_TESTS TRUE ) else() set( BUILD_TESTS FALSE ) diff --git a/cmake/XRootDSummary.cmake b/cmake/XRootDSummary.cmake index c7bd4db2f37..881084282ce 100644 --- a/cmake/XRootDSummary.cmake +++ b/cmake/XRootDSummary.cmake @@ -10,7 +10,7 @@ component_status( MACAROONS BUILD_MACAROONS MACAROONS_FOUND AND JSON_FOUND AND component_status( PYTHON BUILD_PYTHON Python_Interpreter_FOUND AND Python_Development_FOUND ) component_status( READLINE ENABLE_READLINE READLINE_FOUND ) component_status( SCITOKENS BUILD_SCITOKENS SCITOKENSCPP_FOUND ) -component_status( TESTS BUILD_TESTS CPPUNIT_FOUND AND GTEST_FOUND ) +component_status( TESTS BUILD_TESTS GTEST_FOUND ) component_status( TPC BUILD_TPC CURL_FOUND ) component_status( VOMSXRD BUILD_VOMS VOMS_FOUND ) component_status( XRDCL ENABLE_XRDCL TRUE_VAR ) diff --git a/debian/control b/debian/control index f447fe51b9c..8eaf3f9371d 100644 --- a/debian/control +++ b/debian/control @@ -8,7 +8,6 @@ Build-Depends: attr, cmake, libgtest-dev, - libcppunit-dev, libisal-dev, pkg-config, libfuse-dev [linux-any kfreebsd-any], diff --git a/xrootd.spec b/xrootd.spec index b07567e2707..0859febe4e6 100644 --- a/xrootd.spec +++ b/xrootd.spec @@ -47,11 +47,6 @@ Source1: %{url}/download/v%{compat_version}/%{name}-%{compat_version}.tar.gz %undefine __cmake3_in_source_build %endif -%if %{with tests} -# CppUnit crashes with LTO enabled -%global _lto_cflags %nil -%endif - %if %{?rhel}%{!?rhel:0} == 7 BuildRequires: cmake3 BuildRequires: %{devtoolset}-toolchain @@ -134,7 +129,6 @@ BuildRequires: openssl-devel %if %{with tests} BuildRequires: attr -BuildRequires: cppunit-devel BuildRequires: gtest-devel BuildRequires: openssl %endif From 8af03ca58b384b8788701df57587d4744b987dcf Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Thu, 8 Feb 2024 16:16:33 +0100 Subject: [PATCH 019/276] [CI] Remove CppUnit as dependency from GitHub Actions --- .github/workflows/CI.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 00a9de9fab8..5599a5de7c1 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -44,7 +44,6 @@ jobs: apk add \ bash \ cmake \ - cppunit-dev \ ceph-dev \ curl-dev \ fuse-dev \ @@ -249,7 +248,6 @@ jobs: clang \ davix-dev \ g++ \ - libcppunit-dev \ libcurl4-openssl-dev \ libfuse-dev \ libgtest-dev \ @@ -310,7 +308,7 @@ jobs: run: sudo sed -i -e "s/localhost/localhost $(hostname)/g" /etc/hosts - name: Install dependencies with Homebrew - run: brew install cppunit davix googletest isa-l + run: brew install davix googletest isa-l - name: Install Python dependencies with pip run: python3 -m pip install --upgrade pip setuptools wheel From 7a31797f9aa53ca826bb633ba4297cf6984e5c2e Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Thu, 8 Feb 2024 16:21:48 +0100 Subject: [PATCH 020/276] Update documentation to remove references to CppUnit --- docs/INSTALL.md | 5 +---- docs/TESTING.md | 17 ++++++++--------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/docs/INSTALL.md b/docs/INSTALL.md index b594ebcee2c..653cec35df8 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -20,7 +20,7 @@ together with the dependencies required to enable them. | SciTokens | scitokens-cpp-devel | | systemd support | systemd-devel | | VOMS | voms-devel | -| Testing | cppunit-devel gtest-devel | +| Testing | gtest-devel | Other optional dependencies: tinyxml-devel, libxml2-devel. These have bundled copies which are used if not found in the system. In the following sections, we @@ -46,7 +46,6 @@ depending on the distribution: ```sh dnf install \ cmake \ - cppunit-devel \ curl-devel \ davix-devel \ diffutils \ @@ -90,7 +89,6 @@ apt install \ cmake \ davix-dev \ g++ \ - libcppunit-dev \ libcurl4-openssl-dev \ libfuse-dev \ libgtest-dev \ @@ -121,7 +119,6 @@ install dependencies as well when building XRootD from source: ```sh brew install \ cmake \ - cppunit \ curl \ davix \ gcc \ diff --git a/docs/TESTING.md b/docs/TESTING.md index 8bbdf98b08d..6b22cc96ffc 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -11,15 +11,14 @@ list of optional features and which dependencies are required to enable them. ### Enabling tests during CMake configuration -XRootD unit and integration tests are enabled via the CMake configuration -option `-DENABLE_TESTS=ON`. Unit and integration tests may depend on CppUnit -or GoogleTest (a migration from CppUnit to GoogleTest is ongoing). Therefore, -the development packages for CppUnit and GoogleTest (i.e. `cppunit-devel` and -`gtest-devel` on RPM-based distributions) are needed in order to enable all -available tests. Here we discuss how to use the [test.cmake](../test.cmake) -CTest script to run all steps to configure and build the project, then run all -tests using CTest. The script [test.cmake](../test.cmake) can be generically -called from the top directory of the repository as shown below +XRootD unit and integration tests are enabled via the CMake configuration option +`-DENABLE_TESTS=ON`. Unit and integration tests may depend on GoogleTest. +Therefore, the development packages for GoogleTest (i.e. `gtest-devel` or +equivalent) are needed in order to enable all available tests. Here we discuss +how to use the [test.cmake](../test.cmake) CTest script to run all steps to +configure and build the project, then run all tests using CTest. The script +[test.cmake](../test.cmake) can be generically called from the top directory of +the repository as shown below ```sh xrootd $ ctest -V -S test.cmake From 01b16a2dc1acbc1b62c4432b280a15f1b5ad1b65 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Fri, 9 Feb 2024 13:39:09 +0100 Subject: [PATCH 021/276] [CMake] Enable XrdEc by default and use isa-l from the system --- cmake/XRootDDefaults.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/XRootDDefaults.cmake b/cmake/XRootDDefaults.cmake index 4dd9af38f4d..ede87eec391 100644 --- a/cmake/XRootDDefaults.cmake +++ b/cmake/XRootDDefaults.cmake @@ -24,14 +24,14 @@ option( XRDCL_ONLY "Build only the client and necessary dependencies" option( XRDCL_LIB_ONLY "Build only the client libraries and necessary dependencies" FALSE ) option( PYPI_BUILD "The project is being built for PyPI release" FALSE ) option( ENABLE_VOMS "Enable VOMS plug-in if possible." TRUE ) -option( ENABLE_XRDEC "Enable erasure coding component." FALSE ) +option( ENABLE_XRDEC "Enable erasure coding component." TRUE ) option( ENABLE_ASAN "Enable adress sanitizer." FALSE ) option( ENABLE_TSAN "Enable thread sanitizer." FALSE ) option( ENABLE_XRDCLHTTP "Enable xrdcl-http plugin." TRUE ) cmake_dependent_option( ENABLE_SCITOKENS "Enable SciTokens plugin." TRUE "NOT XRDCL_ONLY" FALSE ) cmake_dependent_option( ENABLE_MACAROONS "Enable Macaroons plugin." TRUE "NOT XRDCL_ONLY" FALSE ) option( FORCE_ENABLED "Fail build if enabled components cannot be built." FALSE ) -cmake_dependent_option( USE_SYSTEM_ISAL "Use isa-l installed in the system" FALSE "ENABLE_XRDEC" FALSE ) +cmake_dependent_option( USE_SYSTEM_ISAL "Use isa-l installed in the system" TRUE "ENABLE_XRDEC" FALSE ) define_default( XRD_PYTHON_REQ_VERSION 3 ) # backward compatibility From 44e233c10d501fef6389df722b01dfa01cbd0732 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Thu, 15 Feb 2024 13:51:39 +0100 Subject: [PATCH 022/276] [XrdCeph] Include instead of Fixes warning below: In file included from xrootd/src/XrdCeph/XrdCephPosix.cc:31: include/sys/errno.h:1:2: warning: #warning redirecting incorrect #include to [-Wcpp] --- src/XrdCeph/XrdCephPosix.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/XrdCeph/XrdCephPosix.cc b/src/XrdCeph/XrdCephPosix.cc index 2e31e528774..d9c085e2500 100644 --- a/src/XrdCeph/XrdCephPosix.cc +++ b/src/XrdCeph/XrdCephPosix.cc @@ -28,7 +28,7 @@ #include #include -#include +#include #include #include #include From a5cfef7b28bbb31fb184077a455ad19aae77a3d1 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Sun, 18 Feb 2024 12:24:42 +0100 Subject: [PATCH 023/276] [CMake] Add new option to allow disabling server tests In some cases, for example, when the build directory is on tmpfs and the kernel was built without support for extended attributes on tmpfs, the server tests would fail. This option is to allow one to disable the server tests in such cases. See issues #2096 and #2196. --- cmake/XRootDDefaults.cmake | 7 +++++++ cmake/XRootDSummary.cmake | 2 ++ tests/CMakeLists.txt | 5 +---- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/cmake/XRootDDefaults.cmake b/cmake/XRootDDefaults.cmake index ede87eec391..5d07603b37a 100644 --- a/cmake/XRootDDefaults.cmake +++ b/cmake/XRootDDefaults.cmake @@ -18,6 +18,7 @@ option( ENABLE_KRB5 "Enable the Kerberos 5 authentication if possible." option( ENABLE_READLINE "Enable the lib readline support in the commandline utilities." TRUE ) option( ENABLE_XRDCL "Enable XRootD client." TRUE ) option( ENABLE_TESTS "Enable unit tests." FALSE ) +cmake_dependent_option( ENABLE_SERVER_TESTS "Enable server tests." TRUE "ENABLE_TESTS" FALSE ) option( ENABLE_HTTP "Enable HTTP component." TRUE ) option( ENABLE_PYTHON "Enable python bindings." TRUE ) option( XRDCL_ONLY "Build only the client and necessary dependencies" FALSE ) @@ -38,3 +39,9 @@ define_default( XRD_PYTHON_REQ_VERSION 3 ) if(XRDCEPH_SUBMODULE) set(ENABLE_CEPH TRUE) endif() + +execute_process(COMMAND id -u OUTPUT_VARIABLE UID OUTPUT_STRIP_TRAILING_WHITESPACE) + +if(XRDCL_ONLY OR XRDCL_LIB_ONLY OR UID EQUAL 0) + set(ENABLE_SERVER_TESTS FALSE CACHE BOOL "Server not available or running as root" FORCE) +endif() diff --git a/cmake/XRootDSummary.cmake b/cmake/XRootDSummary.cmake index 881084282ce..3465556d833 100644 --- a/cmake/XRootDSummary.cmake +++ b/cmake/XRootDSummary.cmake @@ -10,6 +10,7 @@ component_status( MACAROONS BUILD_MACAROONS MACAROONS_FOUND AND JSON_FOUND AND component_status( PYTHON BUILD_PYTHON Python_Interpreter_FOUND AND Python_Development_FOUND ) component_status( READLINE ENABLE_READLINE READLINE_FOUND ) component_status( SCITOKENS BUILD_SCITOKENS SCITOKENSCPP_FOUND ) +component_status( SERVER_TESTS ENABLE_SERVER_TESTS TRUE_VAR ) component_status( TESTS BUILD_TESTS GTEST_FOUND ) component_status( TPC BUILD_TPC CURL_FOUND ) component_status( VOMSXRD BUILD_VOMS VOMS_FOUND ) @@ -38,6 +39,7 @@ message( STATUS "Erasure coding: " ${STATUS_XRDEC} ) message( STATUS "Macaroons: " ${STATUS_MACAROONS} ) message( STATUS "SciTokens: " ${STATUS_SCITOKENS} ) message( STATUS "Tests: " ${STATUS_TESTS} ) +message( STATUS "Server Tests: " ${STATUS_SERVER_TESTS} ) message( STATUS "----------------------------------------" ) if( FORCE_ENABLED ) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bb88cc32e26..3648cbfdb7e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,10 +14,7 @@ add_subdirectory(XrdHttpTests) add_subdirectory( XrdSsiTests ) -execute_process(COMMAND id -u OUTPUT_VARIABLE UID - OUTPUT_STRIP_TRAILING_WHITESPACE) - -if(XRDCL_ONLY OR UID EQUAL 0) +if(NOT ENABLE_SERVER_TESTS) return() endif() From 8bbc07f4f39072add91ecc2c97347c49f004703a Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 20 Feb 2024 15:07:27 +0100 Subject: [PATCH 024/276] [XrdTls] Restrict renegotiation for TLSv1.2 and earlier Fixes: #1689 --- src/XrdTls/XrdTlsContext.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/XrdTls/XrdTlsContext.cc b/src/XrdTls/XrdTlsContext.cc index 1d82795d2eb..d9ddf50a00e 100644 --- a/src/XrdTls/XrdTlsContext.cc +++ b/src/XrdTls/XrdTlsContext.cc @@ -591,8 +591,14 @@ XrdTlsContext::XrdTlsContext(const char *cert, const char *key, SSL_CTX **ctxLoc; } ctx_tracker(&pImpl->ctx); - static const int sslOpts = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 - | SSL_OP_NO_COMPRESSION; + static const int sslOpts = SSL_OP_ALL + | SSL_OP_NO_SSLv2 + | SSL_OP_NO_SSLv3 + | SSL_OP_NO_COMPRESSION +#if OPENSSL_VERSION_NUMBER >= 0x10101000L + | SSL_OP_NO_RENEGOTIATION +#endif + ; std::string certFN, eText; const char *emsg; From fdb8c6746935fd3b34a157ff1944723511c29a8d Mon Sep 17 00:00:00 2001 From: Andrew Hanushevsky Date: Tue, 12 Mar 2024 18:35:18 -0700 Subject: [PATCH 025/276] [Server] Pass the kXR_seqio option all the way to the Oss plugin. --- src/XrdOfs/XrdOfs.cc | 8 ++++++++ src/XrdSfs/XrdSfsInterface.hh | 1 + src/XrdXrootd/XrdXrootdXeq.cc | 1 + 3 files changed, 10 insertions(+) diff --git a/src/XrdOfs/XrdOfs.cc b/src/XrdOfs/XrdOfs.cc index 6d829977344..8d91eb7e033 100644 --- a/src/XrdOfs/XrdOfs.cc +++ b/src/XrdOfs/XrdOfs.cc @@ -465,6 +465,7 @@ int XrdOfsFile::open(const char *path, // In SFS_O_CREAT - Create the file open in RW mode SFS_O_TRUNC - Trunc the file open in RW mode SFS_O_POSC - Presist file on successful close + SFS_O_SEQIO - Primarily sequential I/O (e.g. xrdcp) Mode - The Posix access mode bits to be assigned to the file. These bits correspond to the standard Unix permission bits (e.g., 744 == "rwxr--r--"). Additionally, Mode @@ -723,6 +724,13 @@ int XrdOfsFile::open(const char *path, // In error.getUCap() & XrdOucEI::uLclF) open_flag |= O_DIRECT; } +// Pass across the sequential I/O hint. We don't support that for MacOS because +// the open flag we co-opt does not exist there and MacOS can't do it anyway. +// +#ifndef __APPLE__ + if (open_mode & SFS_O_SEQIO) open_flag |= O_RSYNC; +#endif + // Open the file // if ((retc = oP.fP->Open(path, open_flag, theMode, Open_Env))) diff --git a/src/XrdSfs/XrdSfsInterface.hh b/src/XrdSfs/XrdSfsInterface.hh index c0ec1cb9a5d..a94f837844d 100644 --- a/src/XrdSfs/XrdSfsInterface.hh +++ b/src/XrdSfs/XrdSfsInterface.hh @@ -63,6 +63,7 @@ #define SFS_O_RAWIO 0x02000000 // allow client-side decompression #define SFS_O_RESET 0x04000000 // Reset any cached information #define SFS_O_REPLICA 0x08000000 // Open for replication +#define SFS_O_SEQIO 0x10000000 // Open for sequential I/O // The following flag may be set in the access mode arg for open() & mkdir() // Note that on some systems mode_t is 16-bits so we use a careful value! diff --git a/src/XrdXrootd/XrdXrootdXeq.cc b/src/XrdXrootd/XrdXrootdXeq.cc index 5efe8df9162..83a9341c782 100644 --- a/src/XrdXrootd/XrdXrootdXeq.cc +++ b/src/XrdXrootd/XrdXrootdXeq.cc @@ -1438,6 +1438,7 @@ int XrdXrootdProtocol::do_Open() } if (opts & kXR_retstat) {*op++ = 't'; retStat = 1;} if (opts & kXR_posc) {*op++ = 'p'; openopts |= SFS_O_POSC;} + if (opts & kXR_seqio) {*op++ = 'S'; openopts |= SFS_O_SEQIO;} *op = '\0'; TRACEP(FS, "open " < Date: Wed, 13 Mar 2024 17:33:07 -0700 Subject: [PATCH 026/276] [VOMS] Allow VOMS config to use set variables. Fixes #2200 --- src/XrdVoms/XrdVomsMapfile.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/XrdVoms/XrdVomsMapfile.cc b/src/XrdVoms/XrdVomsMapfile.cc index a93ad71e46b..dc881cd0be5 100644 --- a/src/XrdVoms/XrdVomsMapfile.cc +++ b/src/XrdVoms/XrdVomsMapfile.cc @@ -324,7 +324,8 @@ XrdVomsMapfile::Configure(XrdSysError *erp) if (!XrdOucEnv::Import("XRDCONFIGFN", config_filename)) { return VOMS_MAP_FAILED; } - XrdOucStream stream(erp, getenv("XRDINSTANCE")); + XrdOucEnv myEnv; + XrdOucStream stream(erp, getenv("XRDINSTANCE"), &myEnv, "=====> "); int cfg_fd; if ((cfg_fd = open(config_filename, O_RDONLY, 0)) < 0) { From 912b908e02500e2e5c953c08ccc038f3bea69ac1 Mon Sep 17 00:00:00 2001 From: Andrew Hanushevsky Date: Wed, 13 Mar 2024 23:49:59 -0700 Subject: [PATCH 027/276] [Server] Check kXR_seqio to an opt-in strategy. --- src/XrdOfs/XrdOfs.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/XrdOfs/XrdOfs.cc b/src/XrdOfs/XrdOfs.cc index 8d91eb7e033..86e87b7fe4e 100644 --- a/src/XrdOfs/XrdOfs.cc +++ b/src/XrdOfs/XrdOfs.cc @@ -724,10 +724,10 @@ int XrdOfsFile::open(const char *path, // In error.getUCap() & XrdOucEI::uLclF) open_flag |= O_DIRECT; } -// Pass across the sequential I/O hint. We don't support that for MacOS because -// the open flag we co-opt does not exist there and MacOS can't do it anyway. +// Pass across the sequential I/O hint. We only support that for Linux because +// the open flag we co-opted does not exist elsewhere and those can't do much. // -#ifndef __APPLE__ +#ifdef __linux__ if (open_mode & SFS_O_SEQIO) open_flag |= O_RSYNC; #endif From b440a9e0cb653573212fa5905fdbbe7e828f466e Mon Sep 17 00:00:00 2001 From: Brian Bockelman Date: Thu, 29 Feb 2024 22:15:02 -0600 Subject: [PATCH 028/276] Add monitoring packet for IO, based on the throttle plugin Given the throttle plugin is collecting timing statistics for I/O anyway, have it begin to report these statistics out through the g-stream. This allows the monitoring programs to determine the overall time spent in the I/O subsystem. --- src/XrdThrottle/XrdThrottle.hh | 7 ++-- .../XrdThrottleFileSystemConfig.cc | 32 ++++++++++++--- src/XrdThrottle/XrdThrottleManager.cc | 39 ++++++++++++++----- src/XrdThrottle/XrdThrottleManager.hh | 12 +++++- src/XrdXrootd/XrdXrootdConfigMon.cc | 37 ++++++++++-------- src/XrdXrootd/XrdXrootdMonData.hh | 1 + src/XrdXrootd/XrdXrootdMonitor.hh | 3 +- 7 files changed, 94 insertions(+), 37 deletions(-) diff --git a/src/XrdThrottle/XrdThrottle.hh b/src/XrdThrottle/XrdThrottle.hh index 9ddc890f373..cccd21229cd 100644 --- a/src/XrdThrottle/XrdThrottle.hh +++ b/src/XrdThrottle/XrdThrottle.hh @@ -135,7 +135,7 @@ private: class FileSystem : public XrdSfsFileSystem { -friend XrdSfsFileSystem * XrdSfsGetFileSystem_Internal(XrdSfsFileSystem *, XrdSysLogger *, const char *); +friend XrdSfsFileSystem * XrdSfsGetFileSystem_Internal(XrdSfsFileSystem *, XrdSysLogger *, const char *, XrdOucEnv *); public: @@ -260,14 +260,15 @@ public: const char *opaque = 0); virtual int - Configure(XrdSysError &, XrdSfsFileSystem *native_fs); + Configure(XrdSysError &, XrdSfsFileSystem *native_fs, XrdOucEnv *envP); private: static void Initialize( FileSystem *&fs, XrdSfsFileSystem *native_fs, XrdSysLogger *lp, - const char *config_file); + const char *config_file, + XrdOucEnv *envP); FileSystem(); diff --git a/src/XrdThrottle/XrdThrottleFileSystemConfig.cc b/src/XrdThrottle/XrdThrottleFileSystemConfig.cc index 5d1ef9f3bda..ad1cfbd802a 100644 --- a/src/XrdThrottle/XrdThrottleFileSystemConfig.cc +++ b/src/XrdThrottle/XrdThrottleFileSystemConfig.cc @@ -55,10 +55,11 @@ namespace XrdThrottle { XrdSfsFileSystem * XrdSfsGetFileSystem_Internal(XrdSfsFileSystem *native_fs, XrdSysLogger *lp, - const char *configfn) + const char *configfn, + XrdOucEnv *envP) { FileSystem* fs = NULL; - FileSystem::Initialize(fs, native_fs, lp, configfn); + FileSystem::Initialize(fs, native_fs, lp, configfn, envP); return fs; } } @@ -70,11 +71,21 @@ XrdSfsGetFileSystem(XrdSfsFileSystem *native_fs, XrdSysLogger *lp, const char *configfn) { - return XrdSfsGetFileSystem_Internal(native_fs, lp, configfn); + return XrdSfsGetFileSystem_Internal(native_fs, lp, configfn, nullptr); +} + +XrdSfsFileSystem * +XrdSfsGetFileSystem2(XrdSfsFileSystem *native_fs, + XrdSysLogger *lp, + const char *configfn, + XrdOucEnv *envP) +{ + return XrdSfsGetFileSystem_Internal(native_fs, lp, configfn, envP); } } XrdVERSIONINFO(XrdSfsGetFileSystem, FileSystem); +XrdVERSIONINFO(XrdSfsGetFileSystem2, FileSystem); FileSystem* FileSystem::m_instance = 0; @@ -90,7 +101,8 @@ void FileSystem::Initialize(FileSystem *&fs, XrdSfsFileSystem *native_fs, XrdSysLogger *lp, - const char *configfn) + const char *configfn, + XrdOucEnv *envP) { fs = NULL; if (m_instance == NULL && !(m_instance = new FileSystem())) @@ -103,7 +115,7 @@ FileSystem::Initialize(FileSystem *&fs, fs->m_config_file = configfn; fs->m_eroute.logger(lp); fs->m_eroute.Say("Initializing a Throttled file system."); - if (fs->Configure(fs->m_eroute, native_fs)) + if (fs->Configure(fs->m_eroute, native_fs, envP)) { fs->m_eroute.Say("Initialization of throttled file system failed."); fs = NULL; @@ -116,7 +128,7 @@ FileSystem::Initialize(FileSystem *&fs, #define TS_Xeq(key, func) NoGo = (strcmp(key, var) == 0) ? func(Config) : 0 int -FileSystem::Configure(XrdSysError & log, XrdSfsFileSystem *native_fs) +FileSystem::Configure(XrdSysError & log, XrdSfsFileSystem *native_fs, XrdOucEnv *envP) { XrdOucEnv myEnv; XrdOucStream Config(&m_eroute, getenv("XRDINSTANCE"), &myEnv, "(Throttle Config)> "); @@ -165,6 +177,14 @@ FileSystem::Configure(XrdSysError & log, XrdSfsFileSystem *native_fs) // Overwrite the environment variable saying that throttling is the fslib. XrdOucEnv::Export("XRDOFSLIB", fslib.c_str()); + if (envP) + { + auto gstream = reinterpret_cast(envP->GetPtr("Throttle.gStream*")); + log.Say("Config", "Throttle g-stream has", gstream ? "" : " NOT", " been configured via xrootd.mongstream directive"); + m_throttle.SetMonitor(gstream); + } + + return 0; } diff --git a/src/XrdThrottle/XrdThrottleManager.cc b/src/XrdThrottle/XrdThrottleManager.cc index 0bca3f1fc4d..651bd6d9baf 100644 --- a/src/XrdThrottle/XrdThrottleManager.cc +++ b/src/XrdThrottle/XrdThrottleManager.cc @@ -1,11 +1,11 @@ #include "XrdThrottleManager.hh" +#include "XrdOuc/XrdOucEnv.hh" #include "XrdSys/XrdSysAtomics.hh" #include "XrdSys/XrdSysTimer.hh" #include "XrdSys/XrdSysPthread.hh" - -#include "XrdOuc/XrdOucEnv.hh" +#include "XrdXrootd/XrdXrootdGStream.hh" #define XRD_TRACE m_trace-> #include "XrdThrottle/XrdThrottleTrace.hh" @@ -33,7 +33,7 @@ XrdThrottleManager::XrdThrottleManager(XrdSysError *lP, XrdOucTrace *tP) : m_ops_per_second(-1), m_concurrency_limit(-1), m_last_round_allocation(100*1024), - m_io_counter(0), + m_io_active(0), m_loadshed_host(""), m_loadshed_port(0), m_loadshed_frequency(0), @@ -430,7 +430,10 @@ XrdThrottleManager::RecomputeInternal() // Update the IO counters m_compute_var.Lock(); - m_stable_io_counter = AtomicGet(m_io_counter); + m_stable_io_active = AtomicGet(m_io_active); + auto io_active = m_stable_io_active; + m_stable_io_total = static_cast(AtomicGet(m_io_total)); + auto io_total = m_stable_io_total; time_t secs; AtomicFZAP(secs, m_io_wait.tv_sec); long nsecs; AtomicFZAP(nsecs, m_io_wait.tv_nsec); m_stable_io_wait.tv_sec += static_cast(secs * intervals_per_second); @@ -440,8 +443,25 @@ XrdThrottleManager::RecomputeInternal() m_stable_io_wait.tv_nsec -= 1000000000; m_stable_io_wait.tv_nsec --; } + struct timespec io_wait_ts; + io_wait_ts.tv_sec = m_stable_io_wait.tv_sec; + io_wait_ts.tv_nsec = m_stable_io_wait.tv_nsec; + m_compute_var.UnLock(); - TRACE(IOLOAD, "Current IO counter is " << m_stable_io_counter << "; total IO wait time is " << (m_stable_io_wait.tv_sec*1000+m_stable_io_wait.tv_nsec/1000000) << "ms."); + uint64_t io_wait_ms = io_wait_ts.tv_sec*1000+io_wait_ts.tv_nsec/1000000; + TRACE(IOLOAD, "Current IO counter is " << io_active << "; total IO wait time is " << io_wait_ms << "ms."); + if (m_gstream) + { + char buf[128]; + auto len = snprintf(buf, 128, + R"({"event":"throttle_update","io_wait":%.4f,"io_active":%d,"io_total":%d})", + static_cast(io_wait_ms) / 1000.0, io_active, io_total); + auto suc = (len < 128) ? m_gstream->Insert(buf, len + 1) : false; + if (!suc) + { + TRACE(IOLOAD, "Failed g-stream insertion of throttle_update record (len=" << len << "): " << buf); + } + } m_compute_var.Broadcast(); } @@ -470,17 +490,18 @@ XrdThrottleTimer XrdThrottleManager::StartIOTimer() { AtomicBeg(m_compute_var); - int cur_counter = AtomicInc(m_io_counter); + int cur_counter = AtomicInc(m_io_active); + AtomicInc(m_io_total); AtomicEnd(m_compute_var); while (m_concurrency_limit >= 0 && cur_counter > m_concurrency_limit) { AtomicBeg(m_compute_var); AtomicInc(m_loadshed_limit_hit); - AtomicDec(m_io_counter); + AtomicDec(m_io_active); AtomicEnd(m_compute_var); m_compute_var.Wait(); AtomicBeg(m_compute_var); - cur_counter = AtomicInc(m_io_counter); + cur_counter = AtomicInc(m_io_active); AtomicEnd(m_compute_var); } return XrdThrottleTimer(*this); @@ -493,7 +514,7 @@ void XrdThrottleManager::StopIOTimer(struct timespec timer) { AtomicBeg(m_compute_var); - AtomicDec(m_io_counter); + AtomicDec(m_io_active); AtomicAdd(m_io_wait.tv_sec, timer.tv_sec); // Note this may result in tv_nsec > 1e9 AtomicAdd(m_io_wait.tv_nsec, timer.tv_nsec); diff --git a/src/XrdThrottle/XrdThrottleManager.hh b/src/XrdThrottle/XrdThrottleManager.hh index dd1dc2e7a8a..6f292ac7c82 100644 --- a/src/XrdThrottle/XrdThrottleManager.hh +++ b/src/XrdThrottle/XrdThrottleManager.hh @@ -40,6 +40,7 @@ class XrdSysError; class XrdOucTrace; class XrdThrottleTimer; +class XrdXrootdGStream; class XrdThrottleManager { @@ -68,6 +69,8 @@ void SetMaxOpen(unsigned long max_open) {m_max_open = max_open;} void SetMaxConns(unsigned long max_conns) {m_max_conns = max_conns;} +void SetMonitor(XrdXrootdGStream *gstream) {m_gstream = gstream;} + //int Stats(char *buff, int blen, int do_sync=0) {return m_pool.Stats(buff, blen, do_sync);} static @@ -125,10 +128,12 @@ std::vector m_secondary_ops_shares; int m_last_round_allocation; // Active IO counter -int m_io_counter; +int m_io_active; struct timespec m_io_wait; +unsigned m_io_total{0}; // Stable IO counters - must hold m_compute_var lock when reading/writing; -int m_stable_io_counter; +int m_stable_io_active; +int m_stable_io_total{0}; // It would take ~3 years to overflow a 32-bit unsigned integer at 100Hz of IO operations. struct timespec m_stable_io_wait; // Load shed details @@ -145,6 +150,9 @@ std::unordered_map m_conn_counters; std::unordered_map>> m_active_conns; std::mutex m_file_mutex; +// Monitoring handle, if configured +XrdXrootdGStream* m_gstream{nullptr}; + static const char *TraceID; }; diff --git a/src/XrdXrootd/XrdXrootdConfigMon.cc b/src/XrdXrootd/XrdXrootdConfigMon.cc index d8d48ce64b8..3acc2e150d5 100644 --- a/src/XrdXrootd/XrdXrootdConfigMon.cc +++ b/src/XrdXrootd/XrdXrootdConfigMon.cc @@ -80,14 +80,16 @@ struct MonParms MonParms *MP = 0; struct XrdXrootdGSReal::GSParms gsObj[] = - {{"ccm", 0, XROOTD_MON_CCM, 0, -1, XROOTD_MON_GSCCM, 0, - XrdXrootdGSReal::fmtBin, XrdXrootdGSReal::hdrNorm}, - {"pfc", 0, XROOTD_MON_PFC, 0, -1, XROOTD_MON_GSPFC, 0, - XrdXrootdGSReal::fmtBin, XrdXrootdGSReal::hdrNorm}, - {"TcpMon", 0, XROOTD_MON_TCPMO, 0, -1, XROOTD_MON_GSTCP, 0, - XrdXrootdGSReal::fmtBin, XrdXrootdGSReal::hdrNorm}, - {"Tpc", 0, XROOTD_MON_TPC, 0, -1, XROOTD_MON_GSTPC, 0, - XrdXrootdGSReal::fmtBin, XrdXrootdGSReal::hdrNorm} + {{"ccm", 0, XROOTD_MON_CCM, 0, -1, XROOTD_MON_GSCCM, 0, + XrdXrootdGSReal::fmtBin, XrdXrootdGSReal::hdrNorm}, + {"pfc", 0, XROOTD_MON_PFC, 0, -1, XROOTD_MON_GSPFC, 0, + XrdXrootdGSReal::fmtBin, XrdXrootdGSReal::hdrNorm}, + {"TcpMon", 0, XROOTD_MON_TCPMO, 0, -1, XROOTD_MON_GSTCP, 0, + XrdXrootdGSReal::fmtBin, XrdXrootdGSReal::hdrNorm}, + {"Throttle", 0, XROOTD_MON_THROT, 0, -1, XROOTD_MON_GSTHR, 0, + XrdXrootdGSReal::fmtBin, XrdXrootdGSReal::hdrNorm}, + {"Tpc", 0, XROOTD_MON_TPC, 0, -1, XROOTD_MON_GSTPC, 0, + XrdXrootdGSReal::fmtBin, XrdXrootdGSReal::hdrNorm} }; } @@ -100,7 +102,7 @@ bool XrdXrootdProtocol::ConfigGStream(XrdOucEnv &myEnv, XrdOucEnv *urEnv) XrdXrootdGStream *gs; static const int numgs=sizeof(gsObj)/sizeof(struct XrdXrootdGSReal::GSParms); char vbuff[64]; - bool aOK, gXrd[numgs] = {false, false, true, true}; + bool aOK, gXrd[numgs] = {false, false, true, false, true}; // For each enabled monitoring provider, allocate a g-stream and put // its address in our environment. @@ -190,7 +192,7 @@ bool XrdXrootdProtocol::ConfigMon(XrdProtocol_Config *pi, XrdOucEnv &xrootdEnv) [rbuff ] [rnums ] [window ] [dest [Events] ] - Events: [ccm] [files] [fstat] [info] [io] [iov] [pfc] [redir] [tcpmon] [user] + Events: [ccm] [files] [fstat] [info] [io] [iov] [pfc] [redir] [tcpmon] [throttle] [user] all enables monitoring for all connections. auth add authentication information to "user". @@ -222,6 +224,7 @@ bool XrdXrootdProtocol::ConfigMon(XrdProtocol_Config *pi, XrdOucEnv &xrootdEnv) pfc monitor proxy file cache redir monitors request redirections tcpmon monitors tcp connection closes. + throttle monitors I/O activity via the throttle plugin tpc Third Party Copy user monitors user login and disconnect events. where monitor records are to be sentvia UDP. @@ -350,11 +353,12 @@ int XrdXrootdProtocol::xmon(XrdOucStream &Config) else if (!strcmp("io", val)) MP->monMode[i] |= XROOTD_MON_IO; else if (!strcmp("iov", val)) MP->monMode[i] |= (XROOTD_MON_IO |XROOTD_MON_IOV); - else if (!strcmp("pfc", val)) MP->monMode[i] |= XROOTD_MON_PFC; - else if (!strcmp("redir",val)) MP->monMode[i] |= XROOTD_MON_REDR; - else if (!strcmp("tcpmon",val))MP->monMode[i] |= XROOTD_MON_TCPMO; - else if (!strcmp("tpc", val))MP->monMode[i] |= XROOTD_MON_TPC; - else if (!strcmp("user", val)) MP->monMode[i] |= XROOTD_MON_USER; + else if (!strcmp("pfc", val)) MP->monMode[i] |= XROOTD_MON_PFC; + else if (!strcmp("redir", val)) MP->monMode[i] |= XROOTD_MON_REDR; + else if (!strcmp("tcpmon", val)) MP->monMode[i] |= XROOTD_MON_TCPMO; + else if (!strcmp("throttle", val)) MP->monMode[i] |= XROOTD_MON_THROT; + else if (!strcmp("tpc", val)) MP->monMode[i] |= XROOTD_MON_TPC; + else if (!strcmp("user", val)) MP->monMode[i] |= XROOTD_MON_USER; else break; if (!val) {eDest.Emsg("Config","monitor dest value not specified"); @@ -456,6 +460,7 @@ char *XrdXrootdProtocol::xmondest(const char *what, char *val) ccm gstream: cache context management pfc gstream: proxy file cache tcpmon gstream: tcp connection monitoring + throttle gstream: monitors I/O activity via the throttle plugin tpc gstream: Third Party Copy noXXX do not include information. @@ -478,7 +483,7 @@ int XrdXrootdProtocol::xmongs(XrdOucStream &Config) int numgs = sizeof(gsObj)/sizeof(struct XrdXrootdGSReal::GSParms); int selAll = XROOTD_MON_CCM | XROOTD_MON_PFC | XROOTD_MON_TCPMO - | XROOTD_MON_TPC; + | XROOTD_MON_THROT | XROOTD_MON_TPC; int i, selMon = 0, opt = -1, hdr = -1, fmt = -1, flushVal = -1; long long maxlVal = -1; char *val, *dest = 0; diff --git a/src/XrdXrootd/XrdXrootdMonData.hh b/src/XrdXrootd/XrdXrootdMonData.hh index 1c98a6b4707..82035e7cb17 100644 --- a/src/XrdXrootd/XrdXrootdMonData.hh +++ b/src/XrdXrootd/XrdXrootdMonData.hh @@ -119,6 +119,7 @@ const kXR_char XROOTD_MON_GSCCM = 'M'; // pfc: Cache context mgt info const kXR_char XROOTD_MON_GSPFC = 'C'; // pfc: Cache monitoring info const kXR_char XROOTD_MON_GSTCP = 'T'; // TCP connection statistics const kXR_char XROOTD_MON_GSTPC = 'P'; // TPC Third Party Copy +const kXR_char XROOTD_MON_GSTHR = 'R'; // IO activity from the throttle plugin // The following bits are insert in the low order 4 bits of the MON_REDIRECT // entry code to indicate the actual operation that was requestded. diff --git a/src/XrdXrootd/XrdXrootdMonitor.hh b/src/XrdXrootd/XrdXrootdMonitor.hh index 1c1c142ad63..52205e1825f 100644 --- a/src/XrdXrootd/XrdXrootdMonitor.hh +++ b/src/XrdXrootd/XrdXrootdMonitor.hh @@ -59,7 +59,8 @@ #define XROOTD_MON_PFC 0x00000400 #define XROOTD_MON_TCPMO 0x00000800 #define XROOTD_MON_TPC 0x00001000 -#define XROOTD_MON_GSTRM (XROOTD_MON_CCM | XROOTD_MON_PFC | XROOTD_MON_TCPMO) +#define XROOTD_MON_THROT 0x00002000 +#define XROOTD_MON_GSTRM (XROOTD_MON_CCM | XROOTD_MON_PFC | XROOTD_MON_TCPMO | XROOTD_MON_THROT) #define XROOTD_MON_FSLFN 1 #define XROOTD_MON_FSOPS 2 From a78586f01afc2b4536bd7d394ef13e1f07a3c341 Mon Sep 17 00:00:00 2001 From: Andrew Hanushevsky Date: Sat, 16 Mar 2024 16:03:17 -0700 Subject: [PATCH 029/276] [Server] Implement the kXR_seqio open option for sequential I/O. --- src/XrdOfs/XrdOfs.cc | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/XrdOfs/XrdOfs.cc b/src/XrdOfs/XrdOfs.cc index 86e87b7fe4e..ec1dd314046 100644 --- a/src/XrdOfs/XrdOfs.cc +++ b/src/XrdOfs/XrdOfs.cc @@ -724,13 +724,6 @@ int XrdOfsFile::open(const char *path, // In error.getUCap() & XrdOucEI::uLclF) open_flag |= O_DIRECT; } -// Pass across the sequential I/O hint. We only support that for Linux because -// the open flag we co-opted does not exist elsewhere and those can't do much. -// -#ifdef __linux__ - if (open_mode & SFS_O_SEQIO) open_flag |= O_RSYNC; -#endif - // Open the file // if ((retc = oP.fP->Open(path, open_flag, theMode, Open_Env))) @@ -766,6 +759,15 @@ int XrdOfsFile::open(const char *path, // In oP.hP->Activate(oP.fP); oP.hP->UnLock(); +// If this is being opened for sequential I/O advise the filesystem about it. +// +#ifdef __linux__ + if (!(XrdOfsFS->OssIsProxy) && open_mode & SFS_O_SEQIO) + {int theFD = oP.fP->getFD(); + if (theFD >= 0) posix_fadvise(theFD, 0, 0, POSIX_FADV_SEQUENTIAL); + } +#endif + // Send an open event if we must // if (XrdOfsFS->evsObject) From 432cc122cc65275cd41e664f9602ea3f320675b6 Mon Sep 17 00:00:00 2001 From: Andrew Hanushevsky Date: Sat, 16 Mar 2024 16:26:29 -0700 Subject: [PATCH 030/276] [Server] Harden kXR_seqio implementation. --- src/XrdOfs/XrdOfs.cc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/XrdOfs/XrdOfs.cc b/src/XrdOfs/XrdOfs.cc index ec1dd314046..cb8d89cdaf0 100644 --- a/src/XrdOfs/XrdOfs.cc +++ b/src/XrdOfs/XrdOfs.cc @@ -74,6 +74,7 @@ #include "XrdSys/XrdSysLogger.hh" #include "XrdSys/XrdSysPlatform.hh" #include "XrdSys/XrdSysPthread.hh" +#include "XrdSys/XrdSysRAtomic.hh" #include "XrdOuc/XrdOuca2x.hh" #include "XrdOuc/XrdOucEnv.hh" @@ -761,10 +762,15 @@ int XrdOfsFile::open(const char *path, // In // If this is being opened for sequential I/O advise the filesystem about it. // -#ifdef __linux__ +#if defined(__linux__) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) if (!(XrdOfsFS->OssIsProxy) && open_mode & SFS_O_SEQIO) - {int theFD = oP.fP->getFD(); - if (theFD >= 0) posix_fadvise(theFD, 0, 0, POSIX_FADV_SEQUENTIAL); + {static RAtomic_int fadFails(0); + int theFD = oP.fP->getFD(); + if (theFD >= 0 && fadFails < 4096) + if (posix_fadvise(theFD, 0, 0, POSIX_FADV_SEQUENTIAL) < 0) + {OfsEroute.Emsg(epname, errno, "fadsize for sequential I/O."); + fadFails++; + } } #endif From eded1bbea58c487f39824cf2af521ec63a86e352 Mon Sep 17 00:00:00 2001 From: Mattias Ellert Date: Sat, 16 Mar 2024 21:43:38 +0100 Subject: [PATCH 031/276] Fixes for 64 bit time_t on 32 bit systems --- src/XrdApps/Xrdadler32.cc | 2 +- src/XrdBwm/XrdBwmLogger.cc | 5 +++-- src/XrdHttp/XrdHttpReq.cc | 2 +- src/XrdPfc/XrdPfcConfiguration.cc | 2 +- src/XrdPosix/XrdPosixPreload32.cc | 4 ++++ src/XrdSecsss/XrdSecsssKT.cc | 5 +++-- src/XrdXrootd/XrdXrootdPrepare.cc | 3 ++- src/XrdXrootd/XrdXrootdProtocol.cc | 9 +++++---- 8 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/XrdApps/Xrdadler32.cc b/src/XrdApps/Xrdadler32.cc index 4c4e0068b9e..ab333656ec1 100644 --- a/src/XrdApps/Xrdadler32.cc +++ b/src/XrdApps/Xrdadler32.cc @@ -88,7 +88,7 @@ int fGetXattrAdler32(int fd, const char* attr, char *value) int rc; if (fstat(fd, &st)) return 0; - sprintf(mtime, "%ld", st.st_mtime); + sprintf(mtime, "%lld", (long long) st.st_mtime); #if defined(__linux__) || defined(__GNU__) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) rc = fgetxattr(fd, attr, attr_val, 25); diff --git a/src/XrdBwm/XrdBwmLogger.cc b/src/XrdBwm/XrdBwmLogger.cc index 0064f8b7e67..ee0788396f8 100644 --- a/src/XrdBwm/XrdBwmLogger.cc +++ b/src/XrdBwm/XrdBwmLogger.cc @@ -146,11 +146,12 @@ void XrdBwmLogger::Event(Info &eInfo) tp->Tlen = snprintf(tp->Text, XrdBwmLoggerMsg::msgSize, "%s%s" "%s%s%c" - "%ld%ld%ld" + "%lld%lld%lld" "%d%d%d" "%lld%d%c", eInfo.Tident, eInfo.Lfn, eInfo.lclNode, eInfo.rmtNode, - eInfo.Flow, eInfo.ATime, eInfo.BTime, eInfo.CTime, + eInfo.Flow, (long long) eInfo.ATime, + (long long) eInfo.BTime, (long long) eInfo.CTime, eInfo.numqIn, eInfo.numqOut, eInfo.numqXeq, eInfo.Size, eInfo.ESec, theEOL); diff --git a/src/XrdHttp/XrdHttpReq.cc b/src/XrdHttp/XrdHttpReq.cc index 9a54293b52f..b61587118aa 100644 --- a/src/XrdHttp/XrdHttpReq.cc +++ b/src/XrdHttp/XrdHttpReq.cc @@ -656,7 +656,7 @@ void XrdHttpReq::appendOpaque(XrdOucString &s, XrdSecEntity *secent, char *hash, s += "&xrdhttptime="; char buf[256]; - sprintf(buf, "%ld", tnow); + sprintf(buf, "%lld", (long long) tnow); s += buf; if (secent) { diff --git a/src/XrdPfc/XrdPfcConfiguration.cc b/src/XrdPfc/XrdPfcConfiguration.cc index 8f4bcd064ed..2cdb5869394 100644 --- a/src/XrdPfc/XrdPfcConfiguration.cc +++ b/src/XrdPfc/XrdPfcConfiguration.cc @@ -477,7 +477,7 @@ bool Cache::Config(const char *config_filename, const char *parameters) if (m_configuration.m_cs_UVKeep < 0) strcpy(uvk, "lru"); else - sprintf(uvk, "%ld", m_configuration.m_cs_UVKeep); + sprintf(uvk, "%lld", (long long) m_configuration.m_cs_UVKeep); float rg = (m_configuration.m_RamAbsAvailable) / float(1024*1024*1024); loff = snprintf(buff, sizeof(buff), "Config effective %s pfc configuration:\n" " pfc.cschk %s uvkeep %s\n" diff --git a/src/XrdPosix/XrdPosixPreload32.cc b/src/XrdPosix/XrdPosixPreload32.cc index 3d99ba9d2ee..28c0cacef75 100644 --- a/src/XrdPosix/XrdPosixPreload32.cc +++ b/src/XrdPosix/XrdPosixPreload32.cc @@ -44,6 +44,10 @@ #ifdef _FILE_OFFSET_BITS #undef _FILE_OFFSET_BITS #endif + +#ifdef _TIME_BITS +#undef _TIME_BITS +#endif #endif #define XRDPOSIXPRELOAD32 diff --git a/src/XrdSecsss/XrdSecsssKT.cc b/src/XrdSecsss/XrdSecsssKT.cc index 38b1ada15ab..0071b20f1dc 100644 --- a/src/XrdSecsss/XrdSecsssKT.cc +++ b/src/XrdSecsss/XrdSecsssKT.cc @@ -371,10 +371,11 @@ int XrdSecsssKT::Rewrite(int Keep, int &numKeys, int &numTot, int &numExp) if (ktP->Data.Exp && ktP->Data.Exp <= time(0)) {numExp++; continue;} if (!isKey(ktCurr, ktP, 0)) {ktCurr.NUG(ktP); numID = 0;} else if (Keep && numID >= Keep) continue; - n = sprintf(buff, "%s0 u:%s g:%s n:%s N:%lld c:%ld e:%ld f:%lld k:", + n = sprintf(buff, "%s0 u:%s g:%s n:%s N:%lld c:%lld e:%lld f:%lld k:", (numKeys ? "\n" : ""), ktP->Data.User,ktP->Data.Grup,ktP->Data.Name,ktP->Data.ID, - ktP->Data.Crt, ktP->Data.Exp, ktP->Data.Flags); + (long long) ktP->Data.Crt, (long long) ktP->Data.Exp, + ktP->Data.Flags); numID++; numKeys++; keyB2X(ktP, kbuff); if (write(ktFD, buff, n) < 0 || write(ktFD, kbuff, ktP->Data.Len*2) < 0) break; diff --git a/src/XrdXrootd/XrdXrootdPrepare.cc b/src/XrdXrootd/XrdXrootdPrepare.cc index 8ce494c0762..94700936228 100644 --- a/src/XrdXrootd/XrdXrootdPrepare.cc +++ b/src/XrdXrootd/XrdXrootdPrepare.cc @@ -129,7 +129,8 @@ int XrdXrootdPrepare::List(XrdXrootdPrepArgs &pargs, char *resp, int resplen) else continue; if ((up = (char *) index((const char *)(up+1), (int)'_'))) *up = ' '; else continue; - return snprintf(resp, resplen-1, "%s %ld", dp->d_name, buf.st_mtime); + return snprintf(resp, resplen-1, "%s %lld", + dp->d_name, (long long) buf.st_mtime); } // Completed diff --git a/src/XrdXrootd/XrdXrootdProtocol.cc b/src/XrdXrootd/XrdXrootdProtocol.cc index e0e50e75d7d..ec98d7d1ed2 100644 --- a/src/XrdXrootd/XrdXrootdProtocol.cc +++ b/src/XrdXrootd/XrdXrootdProtocol.cc @@ -798,8 +798,8 @@ int XrdXrootdProtocol::StatGen(struct stat &buf, char *xxBuff, int xxLen, // Format the default response: // - m = snprintf(xxBuff, xxLen, "%lld %lld %d %ld", - Dev.uuid, fsz, flags, buf.st_mtime); + m = snprintf(xxBuff, xxLen, "%lld %lld %d %lld", + Dev.uuid, fsz, flags, (long long) buf.st_mtime); // if (!xtnd || m >= xxLen) return xxLen; // @@ -808,8 +808,9 @@ int XrdXrootdProtocol::StatGen(struct stat &buf, char *xxBuff, int xxLen, char *origP = xxBuff; char *nullP = xxBuff + m++; xxBuff += m; xxLen -= m; - n = snprintf(xxBuff, xxLen, "%ld %ld %04o ", - buf.st_ctime, buf.st_atime, buf.st_mode&07777); + n = snprintf(xxBuff, xxLen, "%lld %lld %04o ", + (long long) buf.st_ctime, (long long) buf.st_atime, + buf.st_mode&07777); if (n >= xxLen) return m; xxBuff += n; xxLen -= n; From 5ca591165a511ca8b3d34943cceb503d44b41f18 Mon Sep 17 00:00:00 2001 From: Christopher Larrieu Date: Tue, 19 Mar 2024 09:50:51 +0100 Subject: [PATCH 032/276] [XrdOss] Fix check for option noDread in XrdOssDir::Readdir() Need to check if bit for noDread is enabled in dOpts for the check to work correctly. Signed-off-by: Guilherme Amadio --- src/XrdOss/XrdOssApi.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/XrdOss/XrdOssApi.cc b/src/XrdOss/XrdOssApi.cc index 5e453494f55..9a6bdd7dd53 100644 --- a/src/XrdOss/XrdOssApi.cc +++ b/src/XrdOss/XrdOssApi.cc @@ -609,7 +609,7 @@ int XrdOssDir::Readdir(char *buff, int blen) // Simulate the read operation, if need be. // - if (noDread) + if (dOpts & noDread) {if (ateof) *buff = '\0'; else {*buff = '.'; ateof = true;} return XrdOssOK; From 75d1af2aa71373b2b5a215f5723a425719bf1e05 Mon Sep 17 00:00:00 2001 From: Christopher Larrieu Date: Tue, 19 Mar 2024 09:54:29 +0100 Subject: [PATCH 033/276] [XrdOss] Do not set S_IFBLK in XrdOssSys::Stat() Setting this on all paths causes directories to appear as files when using oss.rsscmd option. Signed-off-by: Guilherme Amadio --- src/XrdOss/XrdOssStat.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/XrdOss/XrdOssStat.cc b/src/XrdOss/XrdOssStat.cc index 562a69f3991..67efd6ebef7 100644 --- a/src/XrdOss/XrdOssStat.cc +++ b/src/XrdOss/XrdOssStat.cc @@ -123,7 +123,6 @@ int XrdOssSys::Stat(const char *path, struct stat *buff, int opts, // if ((retc = MSS_Stat(remote_path, buff))) return retc; if (popts & XRDEXP_NOTRW) buff->st_mode &= ro_Mode; - buff->st_mode |= S_IFBLK; return XrdOssOK; } From 77a335d2ce1a9aa7bd0e7365656192d6e74fa143 Mon Sep 17 00:00:00 2001 From: Elvin Sindrilaru Date: Tue, 19 Mar 2024 10:47:08 +0100 Subject: [PATCH 034/276] [XrdTpc] Force HTTP 1.1 for TPC otherwise transfers fail In Alma 9.3 the default curl version is 7.76.1 and according to the documentation [1], starting with 7.62.0 the default HTTP protocol used is HTTP 2 TLS. Since XRootD supports only HTTP 1.1, this leads to the following error whenever a HTTP TPC transfer is attempted: "HTTP library failure: Stream error in the HTTP/2 framing layer" This patch will force curl to use HTTP 1.1 without even attempting HTTP 2. This is a workaround for the currently available curl version since normaly curl should be able to gracefully downgrade from HTTP 2 to 1.1 by itself. [1] https://curl.se/libcurl/c/CURLOPT_HTTP_VERSION.html --- src/XrdTpc/XrdTpcTPC.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/XrdTpc/XrdTpcTPC.cc b/src/XrdTpc/XrdTpcTPC.cc index 49f7dd4477f..7869d236652 100644 --- a/src/XrdTpc/XrdTpcTPC.cc +++ b/src/XrdTpc/XrdTpcTPC.cc @@ -873,6 +873,7 @@ int TPCHandler::ProcessPushReq(const std::string & resource, XrdHttpExtReq &req) return req.SendSimpleResp(rec.status, NULL, NULL, msg, 0); } curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, (long) CURL_HTTP_VERSION_1_1); // curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_setcloexec_callback); curl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, opensocket_callback); curl_easy_setopt(curl, CURLOPT_OPENSOCKETDATA, &rec); @@ -987,6 +988,7 @@ int TPCHandler::ProcessPullReq(const std::string &resource, XrdHttpExtReq &req) curl_easy_setopt(curl, CURLOPT_INTERFACE, ip); } curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, (long) CURL_HTTP_VERSION_1_1); // curl_easy_setopt(curl,CURLOPT_SOCKOPTFUNCTION,sockopt_setcloexec_callback); curl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, opensocket_callback); curl_easy_setopt(curl, CURLOPT_OPENSOCKETDATA, &rec); From 5fda7a9154f939f979241b01c7f6179f85a2f6a1 Mon Sep 17 00:00:00 2001 From: Andrew Hanushevsky Date: Tue, 19 Mar 2024 17:07:28 -0700 Subject: [PATCH 035/276] [Ouc] Extend XrdOucGatherConf to do more boiler plate work and be extendable. --- src/XrdOuc/XrdOucGatherConf.cc | 264 ++++++++++++++++++++++++++++++--- src/XrdOuc/XrdOucGatherConf.hh | 130 ++++++++++++++-- 2 files changed, 359 insertions(+), 35 deletions(-) diff --git a/src/XrdOuc/XrdOucGatherConf.cc b/src/XrdOuc/XrdOucGatherConf.cc index cef0c6c2c53..0d0235d9f99 100644 --- a/src/XrdOuc/XrdOucGatherConf.cc +++ b/src/XrdOuc/XrdOucGatherConf.cc @@ -27,6 +27,8 @@ /* specific prior written permission of the institution or contributor. */ /******************************************************************************/ +#include + #include #include #include @@ -40,21 +42,40 @@ #include "XrdOuc/XrdOucStream.hh" #include "XrdOuc/XrdOucString.hh" #include "XrdOuc/XrdOucTList.hh" +#include "XrdOuc/XrdOucTokenizer.hh" #include "XrdSys/XrdSysError.hh" +/******************************************************************************/ +/* L o c a l O b j e c t s */ +/******************************************************************************/ + +struct XrdOucGatherConfData +{ +XrdOucTokenizer Tokenizer; +XrdSysError *eDest = 0; +XrdOucString lline; +XrdOucTList *Match = 0; +char *gBuff = 0; +bool echobfr = false; + + XrdOucGatherConfData(XrdSysError *eP) + : Tokenizer(0), eDest(eP) {} + ~XrdOucGatherConfData() {} +}; + /******************************************************************************/ /* C o n s t r u c t o r # 1 */ /******************************************************************************/ XrdOucGatherConf::XrdOucGatherConf(const char *want, XrdSysError *errP) - : XrdOucTokenizer(0), eDest(errP), Match(0), gBuff(0) + : gcP(new XrdOucGatherConfData(errP)) { XrdOucString wlist(want), wtoken; int wlen, wPos = 0; while((wPos = wlist.tokenize(wtoken, wPos, ' ')) != -1) {wlen = (wtoken.endswith('.') ? wtoken.length() : 0); - Match = new XrdOucTList(wtoken.c_str(), wlen, Match); + gcP->Match = new XrdOucTList(wtoken.c_str(), wlen, gcP->Match); } } @@ -63,14 +84,14 @@ XrdOucGatherConf::XrdOucGatherConf(const char *want, XrdSysError *errP) /******************************************************************************/ XrdOucGatherConf::XrdOucGatherConf(const char **&want, XrdSysError *errP) - : XrdOucTokenizer(0), eDest(errP), Match(0), gBuff(0) + : gcP(new XrdOucGatherConfData(errP)) { int n, i = 0; while(want[i]) {if ((n = strlen(want[i]))) {if (*(want[i]+(n-1)) != '.') n = 0; - Match = new XrdOucTList(want[i], n, Match); + gcP->Match = new XrdOucTList(want[i], n, gcP->Match); } } } @@ -83,14 +104,40 @@ XrdOucGatherConf::~XrdOucGatherConf() { XrdOucTList *tP; - while((tP = Match)) - {Match = tP->next; + while((tP = gcP->Match)) + {gcP->Match = tP->next; delete tP; } - if (gBuff) free(gBuff); + if (gcP->gBuff) free(gcP->gBuff); +} + +/******************************************************************************/ +/* E c h o L i n e */ +/******************************************************************************/ + +void XrdOucGatherConf::EchoLine() +{ + +// Make sure we can actually display anything +// + if (!(gcP->eDest)) + throw std::invalid_argument("XrdSysError object not supplied!"); + +// Echo only when we have something to echo +// + if (gcP->lline.length()) gcP->eDest->Say("=====> ", gcP->lline.c_str()); } + +/******************************************************************************/ +/* E c h o O r d e r */ +/******************************************************************************/ +void XrdOucGatherConf::EchoOrder(bool doBefore) +{ + gcP->echobfr = doBefore; +} + /******************************************************************************/ /* G a t h e r */ /******************************************************************************/ @@ -98,7 +145,7 @@ XrdOucGatherConf::~XrdOucGatherConf() int XrdOucGatherConf::Gather(const char *cfname, Level lvl, const char *parms) { XrdOucEnv myEnv; - XrdOucStream Config(eDest, getenv("XRDINSTANCE"), &myEnv, "=====> "); + XrdOucStream Config(gcP->eDest, getenv("XRDINSTANCE"), &myEnv, "=====> "); XrdOucTList *tP; XrdOucString theGrab; char *var, drctv[64], body[4096]; @@ -107,17 +154,21 @@ int XrdOucGatherConf::Gather(const char *cfname, Level lvl, const char *parms) // Make sure we have something to compare // - if (!Match) return 0; + if (!(gcP->Match)) return 0; // Reset the buffer if it has been set // - if (gBuff) {free(gBuff); gBuff = 0; Attach(0);} + if (gcP->gBuff) + {free(gcP->gBuff); + gcP->gBuff = 0; + gcP->Tokenizer.Attach(0); + } // Open the config file // if ( (cfgFD = open(cfname, O_RDONLY, 0)) < 0) {rc = errno; - if (eDest) eDest->Emsg("Gcf", rc, "open config file", cfname); + if (gcP->eDest) gcP->eDest->Emsg("Gcf", rc, "open config file", cfname); return -rc; } @@ -141,7 +192,7 @@ int XrdOucGatherConf::Gather(const char *cfname, Level lvl, const char *parms) // Process the config file // while((var = Config.GetMyFirstWord())) - {tP = Match; + {tP = gcP->Match; while(tP && ((tP->val && strncmp(var, tP->text, tP->val)) || (!tP->val && strcmp( var, tP->text)))) tP = tP->next; @@ -153,13 +204,13 @@ int XrdOucGatherConf::Gather(const char *cfname, Level lvl, const char *parms) } int n = snprintf(drctv+1, sizeof(drctv)-1, "%s ", var); if (n >= (int)sizeof(drctv)-1) - {if (eDest) eDest->Emsg("Gcf", E2BIG, "handle", var); + {if (gcP->eDest) gcP->eDest->Emsg("Gcf", E2BIG, "handle", var); return -E2BIG; } } else drctv[1] = 0; if (!Config.GetRest(body, sizeof(body))) - {if (eDest) eDest->Emsg("Gcf", E2BIG, "handle arguments"); + {if (gcP->eDest) gcP->eDest->Emsg("Gcf", E2BIG, "handle arguments"); return -E2BIG; } @@ -173,7 +224,7 @@ int XrdOucGatherConf::Gather(const char *cfname, Level lvl, const char *parms) // Now check if any errors occurred during file i/o // if ((rc = Config.LastError())) - {if (eDest) eDest->Emsg("Gcf", rc, "read config file", cfname); + {if (gcP->eDest) gcP->eDest->Emsg("Gcf", rc, "read config file", cfname); return (rc < 0 ? rc : -rc); } @@ -181,23 +232,192 @@ int XrdOucGatherConf::Gather(const char *cfname, Level lvl, const char *parms) // Copy the grab to a modifiable buffer. // if ((n = theGrab.length()) <= 1) n = 0; - else {gBuff = (char *)malloc(n); - strcpy(gBuff, theGrab.c_str()+1); // skip 1st byte but add null - Attach(gBuff); + else {gcP->gBuff = (char *)malloc(n); + strcpy(gcP->gBuff, theGrab.c_str()+1); // skip 1st byte but add null + gcP->Tokenizer.Attach(gcP->gBuff); n--; } return n; } +/******************************************************************************/ +/* G e t L i n e */ +/******************************************************************************/ + +char* XrdOucGatherConf::GetLine() +{ + char* theLine = gcP->Tokenizer.GetLine(); + + while(theLine && *theLine == 0) theLine = gcP->Tokenizer.GetLine(); + + if (!theLine) gcP->lline = ""; + else gcP->lline = theLine; + + return theLine; +} + +/******************************************************************************/ +/* G e t T o k e n */ +/******************************************************************************/ + +char* XrdOucGatherConf::GetToken(char **rest, int lowcase) +{ + return gcP->Tokenizer.GetToken(rest, lowcase); +} + /******************************************************************************/ /* h a s D a t a */ /******************************************************************************/ bool XrdOucGatherConf::hasData() { - return gBuff != 0 && *gBuff != 0; + return gcP->gBuff != 0 && *(gcP->gBuff) != 0; +} + +/******************************************************************************/ +/* M s g E */ +/******************************************************************************/ + +void XrdOucGatherConf::MsgE(const char* txt1,const char* txt2,const char* txt3, + const char* txt4,const char* txt5,const char* txt6) +{ + const char* mVec[7]; + int n = 0; + + mVec[n++] = "Config mistake:"; + if (txt1) mVec[n++] = txt1; + if (txt2) mVec[n++] = txt2; + if (txt3) mVec[n++] = txt3; + if (txt4) mVec[n++] = txt4; + if (txt5) mVec[n++] = txt5; + if (txt6) mVec[n++] = txt6; + + MsgX(mVec, n+1); +} + +/******************************************************************************/ +/* M s g W */ +/******************************************************************************/ + +void XrdOucGatherConf::MsgW(const char* txt1,const char* txt2,const char* txt3, + const char* txt4,const char* txt5,const char* txt6) +{ + const char* mVec[7]; + int n = 0; + + mVec[n++] = "Config warning:"; + if (txt1) mVec[n++] = txt1; + if (txt2) mVec[n++] = txt2; + if (txt3) mVec[n++] = txt3; + if (txt4) mVec[n++] = txt4; + if (txt5) mVec[n++] = txt5; + if (txt6) mVec[n++] = txt6; + + MsgX(mVec, n+1); } +/******************************************************************************/ +/* M s g X */ +/******************************************************************************/ + +void XrdOucGatherConf::MsgX(const char** mVec, int n) +{ + XrdOucString theMsg(2048); + +// Make sure we can actually display anything +// + if (!(gcP->eDest)) + throw std::invalid_argument("XrdSysError object not supplied!"); + +// Construct the message in a string +// + for (int i = 0; i < n; i++) + {theMsg += mVec[i]; + if (i+1 < n) theMsg += ' '; + } + +// Dislay the last line and the message in the proper order +// + if (gcP->echobfr) EchoLine(); + gcP->eDest->Say(theMsg.c_str()); + if (!(gcP->echobfr)) EchoLine(); +} + +/******************************************************************************/ +/* M s g f E */ +/******************************************************************************/ + +void XrdOucGatherConf::MsgfE(const char *fmt, ...) +{ + char buffer[2048]; + va_list args; + va_start (args, fmt); + +// Format the message +// + vsnprintf(buffer, sizeof(buffer), fmt, args); + +// Go print the message +// + MsgfX("Config mistake: ", buffer); +} + +/******************************************************************************/ +/* M s g f W */ +/******************************************************************************/ + +void XrdOucGatherConf::MsgfW(const char *fmt, ...) +{ + char buffer[2048]; + va_list args; + va_start (args, fmt); + +// Format the message +// + vsnprintf(buffer, sizeof(buffer), fmt, args); + +// Go print the message +// + MsgfX("Config warning: ", buffer); +} + +/******************************************************************************/ +/* M s g f X */ +/******************************************************************************/ + +void XrdOucGatherConf::MsgfX(const char* txt1, const char* txt2) +{ + +// Make sure we can actually display anything +// + if (!(gcP->eDest)) + throw std::invalid_argument("XrdSysError object not supplied!"); + +// Dislay the last line and the message in the proper order +// + if (gcP->echobfr) EchoLine(); + gcP->eDest->Say(txt1, txt2); + if (!(gcP->echobfr)) EchoLine(); +} + +/******************************************************************************/ +/* R e t T o k e n */ +/******************************************************************************/ + +void XrdOucGatherConf::RetToken() +{ + return gcP->Tokenizer.RetToken(); +} + +/******************************************************************************/ +/* T a b s */ +/******************************************************************************/ + +void XrdOucGatherConf::Tabs(int x) +{ + gcP->Tokenizer.Tabs(x); +} + /******************************************************************************/ /* u s e D a t a */ /******************************************************************************/ @@ -206,8 +426,8 @@ bool XrdOucGatherConf::useData(const char *data) { if (!data || *data == 0) return false; - if (gBuff) free(gBuff); - gBuff = strdup(data); - Attach(gBuff); + if (gcP->gBuff) free(gcP->gBuff); + gcP->gBuff = strdup(data); + gcP->Tokenizer.Attach(gcP->gBuff); return true; } diff --git a/src/XrdOuc/XrdOucGatherConf.hh b/src/XrdOuc/XrdOucGatherConf.hh index ce5d6ecd1a7..266607fd169 100644 --- a/src/XrdOuc/XrdOucGatherConf.hh +++ b/src/XrdOuc/XrdOucGatherConf.hh @@ -29,21 +29,38 @@ /* specific prior written permission of the institution or contributor. */ /******************************************************************************/ +struct XrdOucGatherConfData; +class XrdSysError; -#include "XrdOuc/XrdOucTokenizer.hh" - -class XrdOucTList; -class XrdSysError; - -class XrdOucGatherConf : public XrdOucTokenizer +class XrdOucGatherConf { public: +//------------------------------------------------------------------------------ +//! Echo the last line retrieved using GetLine() using proper framing. +//! +//! @notes 1) An exception is thrown if a XrdSysError object was not supplied. +//------------------------------------------------------------------------------ + +void EchoLine(); + +//------------------------------------------------------------------------------ +//! Specift the order in which the last line is displayed vs a message. +//! +//! @paramn doBefore - When true, the line is displayed before the message. +//! When false, it is displayed after the message (default). +//! +//! @notes 1) This call is only relevant to calls to MsgE(), MsgW(), MsgfE(), +//! and MsgfW. +//------------------------------------------------------------------------------ + +void EchoOrder(bool doBefore); + //------------------------------------------------------------------------------ //! Gather information from a config file. //! //! @note You must call this method or a successful useData() before calling -//! any XrdOucTokenizer methods. +//! any Get/Ret methods. //! //! @param cfname Path to the configuration file. //! @param lvl Indicates how the gathered directives are to be saved: @@ -72,6 +89,31 @@ enum Level {full_lines = 0, //!< Complete lines int Gather(const char *cfname, Level lvl, const char *parms=0); +//------------------------------------------------------------------------------ +//! Sequence to the next line in the configuration file. +//! +//! @return Pointer to the next line that will be tokenized or NIL if there +//! are no more lines left. +//! +//! @notes 1) You must call GetLine() before calling GetToken(). +//------------------------------------------------------------------------------ + +char* GetLine(); + +//------------------------------------------------------------------------------ +//! Get the next blank-delimited token in the record returned by Getline(). +//! +//! @param rest - Address of a char pointer. When specified, a pointer to +//! the first non-blank character after the returned token. +//! @param lowcasee - When 1, all characters are converted to lower case. +//! When 0, the default, the characters are not changed. +//! +//! @return A pointer to the next token. If the end of the line has been +//! reached, a NIL pointer is returned. +//------------------------------------------------------------------------------ + +char* GetToken(char **rest=0, int lowcase=0); + //------------------------------------------------------------------------------ //! Check if data is present. //! @@ -80,10 +122,71 @@ int Gather(const char *cfname, Level lvl, const char *parms=0); bool hasData(); +//----------------------------------------------------------------------------- +//! Display a space delimited error/warning message. +//! +//! @param txt1,txt2,txt3,txt4,txt5,txt6 the message components formatted as +//! "txt1 [txt2] [txt3] [txt4] [txt5] [txt6]" +//! +//! @notes 1) This methods throws an exception if a XrdSysError object was not +//! passed to the constructor. +//! 2) The last line returned by this object will be displayed either +//! before or after the message (see EchoOrder()). +//! 3) Messages are truncated at 2048 bytes. +//! 4} Use MsgE for errors. The text is prefixed by "Config mistake:" +//! Use MsgW for warnings. The text is prefixed by "Config warning:" +//----------------------------------------------------------------------------- + +void MsgE(const char* txt1, const char* txt2=0, const char* txt3=0, + const char* txt4=0, const char* txt5=0, const char* txt6=0); + +void MsgW(const char* txt1, const char* txt2=0, const char* txt3=0, + const char* txt4=0, const char* txt5=0, const char* txt6=0); + +//----------------------------------------------------------------------------- +//! Display a formated error/warning message using variable args (i.e. vprintf). +//! +//! @param fmt the message formatting template (i.e. printf format). +//! @param ... the arguments that should be used with the template. The +//! formatted message is truncated at 2048 bytes. +//! +//! @notes 1) This methods throws an exception if a XrdSysError object was not +//! passed to the constructor. +//! 2) The last line returned by this object will be displayed either +//! before or after the message (see EchoOrder()). +//! 3) Messages are truncated at 2048 bytes. +//! 4} Use MsgfE for errors. The text is prefixed by "Config mistake:" +//! Use MsgfW for warnings. The text is prefixed by "Config warning:" +//----------------------------------------------------------------------------- + +void MsgfE(const char *fmt, ...); + +void MsgfW(const char *fmt, ...); + +//------------------------------------------------------------------------------ +//! Backups the token scanner just prior to the last returned token. +//! +//! @notes 1) Only one backup is allowed. Calling RetToken() more than once +//! without an intervening GetToken() call results in undefined +//! behaviour. +//! 2) This call is useful for backing up due to an overscan. +//------------------------------------------------------------------------------ + +void RetToken(); + +//------------------------------------------------------------------------------ +//! Specify how tag characters should be handled. +//! +//! @param x - When 0, tabs are converted to spaces. +//! When 1, tabs are untouched (the default). +//------------------------------------------------------------------------------ + +void Tabs(int x=1); + //------------------------------------------------------------------------------ //! Attempt to use pre-existing data. //! -//! @param data Pointer to null terminated pre-existing data. +//! @param data - Pointer to null terminated pre-existing data. //! //! @return False if the pointer is nil or points to a null string; true o/w. //------------------------------------------------------------------------------ @@ -94,8 +197,8 @@ bool useData(const char *data); //! Constructor #1 //! //! @note This object collects relevant configuration directives ready to be -//! processed by the inherited XrdOucTokenizer methods. All if-fi, set, -//! and variable substitutions are performed. +//! processed by the Get/Ret methods. All if-fi, set, and variable +//! substitutions are performed. //! //! @param want A space separated list of directive prefixes (i.e. end with a //! dot) and actual directives that should be gathered. @@ -125,8 +228,9 @@ bool useData(const char *data); private: -XrdSysError *eDest; -XrdOucTList *Match; -char *gBuff; +void MsgX(const char** mVec, int n); +void MsgfX(const char* txt1, const char* txt2); + +XrdOucGatherConfData *gcP; }; #endif From 0eb71c48a6985865e6e958a857b07bbfd6200bcb Mon Sep 17 00:00:00 2001 From: Andrew Hanushevsky Date: Tue, 19 Mar 2024 17:09:15 -0700 Subject: [PATCH 036/276] sc] Make XrdOucGatherConf.hh a public header. --- src/XrdHeaders.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/src/XrdHeaders.cmake b/src/XrdHeaders.cmake index e07de6f1542..c723a6a7a81 100644 --- a/src/XrdHeaders.cmake +++ b/src/XrdHeaders.cmake @@ -33,6 +33,7 @@ set( XROOTD_PUBLIC_HEADERS XrdOuc/XrdOucDLlist.hh XrdOuc/XrdOucEnv.hh XrdOuc/XrdOucErrInfo.hh + XrdOuc/XrdOucGatherConf.hh XrdOuc/XrdOucGMap.hh XrdOuc/XrdOucHash.hh XrdOuc/XrdOucHash.icc From 881ae0bb5fac022b23950620dff483c42549b837 Mon Sep 17 00:00:00 2001 From: Andrew Hanushevsky Date: Tue, 19 Mar 2024 17:59:11 -0700 Subject: [PATCH 037/276] [Ouc] Proide method to get the last line from XrdOucGatherConf. --- src/XrdOuc/XrdOucGatherConf.cc | 10 ++++++++++ src/XrdOuc/XrdOucGatherConf.hh | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/src/XrdOuc/XrdOucGatherConf.cc b/src/XrdOuc/XrdOucGatherConf.cc index 0d0235d9f99..cf70d4963c0 100644 --- a/src/XrdOuc/XrdOucGatherConf.cc +++ b/src/XrdOuc/XrdOucGatherConf.cc @@ -274,6 +274,16 @@ bool XrdOucGatherConf::hasData() return gcP->gBuff != 0 && *(gcP->gBuff) != 0; } +/******************************************************************************/ +/* L a s t L i n e */ +/******************************************************************************/ + +const char* XrdOucGatherConf::LastLine() +{ + if (gcP->lline.capacity() == 0) return ""; + return gcP->lline.c_str(); +} + /******************************************************************************/ /* M s g E */ /******************************************************************************/ diff --git a/src/XrdOuc/XrdOucGatherConf.hh b/src/XrdOuc/XrdOucGatherConf.hh index 266607fd169..135c5559b38 100644 --- a/src/XrdOuc/XrdOucGatherConf.hh +++ b/src/XrdOuc/XrdOucGatherConf.hh @@ -114,6 +114,15 @@ char* GetLine(); char* GetToken(char **rest=0, int lowcase=0); +//------------------------------------------------------------------------------ +//! Get the last line. +//! +//! @return pointer to the last line. If no last line exists a null string is +//! returned. The pointer is valid until GetLine() is called. +//------------------------------------------------------------------------------ + +const char* LastLine(); + //------------------------------------------------------------------------------ //! Check if data is present. //! From b5d52aa5574b4074d144defb37809bf0a2f84f18 Mon Sep 17 00:00:00 2001 From: Andrew Hanushevsky Date: Tue, 19 Mar 2024 21:53:22 -0700 Subject: [PATCH 038/276] [Posix] Correct xml cache summary report. Fixes #2219 --- src/XrdPosix/XrdPosixConfig.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/XrdPosix/XrdPosixConfig.cc b/src/XrdPosix/XrdPosixConfig.cc index e08c651971c..e43e4125f85 100644 --- a/src/XrdPosix/XrdPosixConfig.cc +++ b/src/XrdPosix/XrdPosixConfig.cc @@ -563,7 +563,7 @@ int XrdPosixConfig::Stats(const char *theID, char *buff, int blen) static const char stats2[] = "" "%lld%lld%lld" "%lld%lld" - "%lld>%lld" + "%lld%lld" "" "%lld%lld" "%lld%lld" From ddff1744438a0872db77b9d6ef2fa8ecd50d3cbc Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Sun, 17 Mar 2024 16:42:27 +0100 Subject: [PATCH 039/276] [CMake] Check result of configuration step and stop if it fails --- test.cmake | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test.cmake b/test.cmake index f86d52b10dc..e0f75a4238f 100644 --- a/test.cmake +++ b/test.cmake @@ -200,10 +200,17 @@ if(EXISTS "${CTEST_GIT_COMMAND}") endif() section("Configure") -ctest_configure(OPTIONS "${CMAKE_ARGS}") +ctest_configure(OPTIONS "${CMAKE_ARGS}" RETURN_VALUE CONFIG_RESULT) ctest_read_custom_files("${CTEST_BINARY_DIRECTORY}") list(APPEND CTEST_NOTES_FILES ${CTEST_BINARY_DIRECTORY}/CMakeCache.txt) + +if(NOT ${CONFIG_RESULT} EQUAL 0) + if(CDASH OR (DEFINED ENV{CDASH} AND "$ENV{CDASH}")) + ctest_submit() + endif() + message(FATAL_ERROR "Configuration failed") +endif() endsection() section("Build") From aed4d7f1b89064283aeaadf0ea31acc1c6880239 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Sun, 17 Mar 2024 16:40:22 +0100 Subject: [PATCH 040/276] [CI] Use mk-build-deps to install dependencies on Ubuntu --- .github/workflows/CI.yml | 35 ++++++----------------------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 5599a5de7c1..ee8da1fa9f9 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -240,42 +240,19 @@ jobs: CMAKE_ARGS: '-DINSTALL_PYTHON_BINDINGS=0;-DUSE_SYSTEM_ISAL=1;-DCMAKE_INSTALL_PREFIX=/usr' steps: - - name: Install dependencies + - name: Install development tools run: | - sudo apt update -q - sudo apt install -y \ - cmake \ - clang \ - davix-dev \ - g++ \ - libcurl4-openssl-dev \ - libfuse-dev \ - libgtest-dev \ - libisal-dev \ - libjson-c-dev \ - libkrb5-dev \ - libmacaroons-dev \ - libreadline-dev \ - libscitokens-dev \ - libssl-dev \ - libsystemd-dev \ - libtinyxml-dev \ - libxml2-dev \ - make \ - pkg-config \ - python3-dev \ - python3-pip \ - python3-setuptools \ - python3-wheel \ - uuid-dev \ - voms-dev \ - zlib1g-dev + sudo apt update -qq + sudo apt install -y build-essential devscripts equivs git - name: Clone repository uses: actions/checkout@v3 with: fetch-depth: 0 + - name: Install XRootD build dependencies + run: mk-build-deps --install --remove -s sudo debian/control <<< yes + - name: Build and Test with CTest run: env CC=${CC} CXX=${CC/g*/g++} ctest -VV -S test.cmake From 387f9b917f7e3292bd895c1d9b1f826ba497ad50 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 19 Mar 2024 13:36:57 +0100 Subject: [PATCH 041/276] [CMake] Add space after pull request user info in CDash build name --- test.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.cmake b/test.cmake index e0f75a4238f..87474fa02db 100644 --- a/test.cmake +++ b/test.cmake @@ -90,7 +90,7 @@ if(DEFINED ENV{GITHUB_ACTIONS}) set(ENV{BASE_REF} $ENV{GITHUB_SHA}^1) set(ENV{HEAD_REF} $ENV{GITHUB_SHA}^2) string(REGEX REPLACE "/merge" "" PR_NUMBER "$ENV{GITHUB_REF_NAME}") - string(PREPEND CTEST_BUILD_NAME "#${PR_NUMBER} ($ENV{GITHUB_ACTOR})") + string(PREPEND CTEST_BUILD_NAME "#${PR_NUMBER} ($ENV{GITHUB_ACTOR}) ") else() set(ENV{HEAD_REF} $ENV{GITHUB_SHA}) string(APPEND CTEST_BUILD_NAME " ($ENV{GITHUB_REF_NAME})") From 0cfa4081feb44b34095ee36512e88fceabc7617d Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Wed, 20 Mar 2024 08:57:33 +0100 Subject: [PATCH 042/276] [XrdClTls] Prevent concurrent calls to InitTLS() The function InitTLS() can be called either from the constructor of XrdCl::Tls(), or from XRootDTransport::InitProtocolReq(), while the protocol initialization is happening. Since these calls can happen concurrently, for example, when called as a socket callback during the initial connection setup, there can be a crash if two threads try to assign to the unique_ptr at the same time. Fixes: #2220 --- src/XrdCl/XrdClTls.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/XrdCl/XrdClTls.cc b/src/XrdCl/XrdClTls.cc index a788b7c2229..7f178f94063 100644 --- a/src/XrdCl/XrdClTls.cc +++ b/src/XrdCl/XrdClTls.cc @@ -27,6 +27,7 @@ #include "XrdTls/XrdTlsContext.hh" #include "XrdOuc/XrdOucUtils.hh" +#include #include #include @@ -94,6 +95,9 @@ namespace XrdCl { bool InitTLS() { + static std::mutex tls_mutex; + std::lock_guard tls_lock(tls_mutex); + if (tlsContext) return true; From 5794cbcdc3ceebcf874eccee72770593cae23b6d Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Wed, 20 Mar 2024 13:25:34 +0100 Subject: [PATCH 043/276] [XrdCeph] Fix clang warning about unused expression result Caused by .first in the second statement. Since we are touching these lines, use map.emplace() to simplify the code as well. --- src/XrdCeph/XrdCephPosix.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/XrdCeph/XrdCephPosix.cc b/src/XrdCeph/XrdCephPosix.cc index d9c085e2500..3eb32a28ef2 100644 --- a/src/XrdCeph/XrdCephPosix.cc +++ b/src/XrdCeph/XrdCephPosix.cc @@ -573,9 +573,8 @@ int checkAndCreateStriper(unsigned int cephPoolIdx, std::string &userAtPool, con return 0; } IOCtxDict & ioDict = g_ioCtx[cephPoolIdx]; - ioDict.insert(std::pair(userAtPool, ioctx)); - sDict.insert(std::pair - (userAtPool, striper)).first; + ioDict.emplace(userAtPool, ioctx); + sDict.emplace(userAtPool, striper); } return 1; } From 90968349e8c980fce4d52adcef3efbfe67c7429d Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Mon, 5 Feb 2024 22:10:55 +0100 Subject: [PATCH 044/276] [CI] Move GitHub Actions builds to actions/checkout@v4 Required due to GitHub Actions migration to node 20: https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20 Note: CentOS 7 checkout fails with v4, so must use v1 (will be removed at EOL later). --- .github/workflows/CI.yml | 14 ++++++-------- .github/workflows/DEB.yml | 4 ++-- .github/workflows/QEMU.yml | 2 +- .github/workflows/RPM.yml | 6 +++--- .github/workflows/python.yml | 2 +- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ee8da1fa9f9..434369197e9 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -71,9 +71,7 @@ jobs: zlib-dev - name: Clone repository - uses: actions/checkout@v3 - with: - fetch-depth: 0 + uses: actions/checkout@v1 - name: Setup GitHub runner user within container run: adduser -D --uid 1001 runner && chown -R runner:runner ${GITHUB_WORKSPACE} @@ -134,7 +132,7 @@ jobs: dnf config-manager --set-enabled powertools - name: Clone repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -170,7 +168,7 @@ jobs: dnf config-manager --set-enabled crb - name: Clone repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -205,7 +203,7 @@ jobs: run: dnf install -y dnf-plugins-core git ninja-build rpmdevtools - name: Clone repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -246,7 +244,7 @@ jobs: sudo apt install -y build-essential devscripts equivs git - name: Clone repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -291,7 +289,7 @@ jobs: run: python3 -m pip install --upgrade pip setuptools wheel - name: Clone repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.github/workflows/DEB.yml b/.github/workflows/DEB.yml index 4f6c7a34f41..a29810084a0 100644 --- a/.github/workflows/DEB.yml +++ b/.github/workflows/DEB.yml @@ -43,7 +43,7 @@ jobs: apt install -y build-essential devscripts git - name: Clone repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -87,7 +87,7 @@ jobs: sudo apt install -y build-essential devscripts equivs git - name: Clone repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.github/workflows/QEMU.yml b/.github/workflows/QEMU.yml index ae9534570da..bedd6c63bbf 100644 --- a/.github/workflows/QEMU.yml +++ b/.github/workflows/QEMU.yml @@ -46,7 +46,7 @@ jobs: steps: - name: Clone repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.github/workflows/RPM.yml b/.github/workflows/RPM.yml index 423a1a4b939..7343780b876 100644 --- a/.github/workflows/RPM.yml +++ b/.github/workflows/RPM.yml @@ -72,7 +72,7 @@ jobs: run: yum install -y git - name: Clone repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -117,7 +117,7 @@ jobs: run: yum install -y git - name: Clone repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -162,7 +162,7 @@ jobs: run: yum install -y git - name: Clone repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index b27d40c20ba..e2bebb71b44 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -39,7 +39,7 @@ jobs: run: dnf install -y git krb5-devel libuuid-devel openssl-devel - name: Clone repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 From bb455752c68143bc7a3d8e79d3cf63c5db9c299a Mon Sep 17 00:00:00 2001 From: Brian Bockelman Date: Sat, 11 Nov 2023 12:14:51 +0100 Subject: [PATCH 045/276] Explicitly control the authorization strategy for tokens In some cases, a token may be mapped to a known user - but we only want to utilize the mapping if there's scope-based authorization in place. This can now be controlled as part of the scitokens.cfg. In the issuer section, one can add: ``` authorization_strategy = strategy1 strategy2 strategy3 ``` Where possible strategies are `capability` (if authorized based on scope, pass through the username mapping), `group` (if authorized based on scope, pass through the group information), and `mapping`, (always pass through the explicit mapping, even if there's no scope present). --- src/XrdSciTokens/README.md | 11 +++++ src/XrdSciTokens/XrdSciTokensAccess.cc | 68 +++++++++++++++++++++----- 2 files changed, 66 insertions(+), 13 deletions(-) diff --git a/src/XrdSciTokens/README.md b/src/XrdSciTokens/README.md index e26199b2c91..b7c69fdbb04 100644 --- a/src/XrdSciTokens/README.md +++ b/src/XrdSciTokens/README.md @@ -117,6 +117,17 @@ are: claim name. If set, it overrides `map_subject` and `default_user`. - `name_mapfile` (options): If set, then the referenced file is parsed as a JSON object and the specified mappings are applied to the username inside the XRootD framework. See below for more information on the mapfile. + - `authorization_strategy` (optional): One or more authorizations to use from the token. Multiple (space separated) + items may be specified from the following valid values: + + - `capability`: Authorize based on capabilities (e.g., `storage.read:/foo`) from the token. + - `group`: Pass through the request if there's any group present in the token. + - `mapping`: Pass through the request if the user mapping was successful. + + For the `group` and `mapping` cases, the username and group are set in the internal XRootD request credential, + but the final authorization must be done by a subsequent plugin. The default value is `capability group mapping`. + *Note*: if `mapping` is present, then a token without a capability may still have authorized actions. + Group- and Scope-based authorization ------------------------------------ diff --git a/src/XrdSciTokens/XrdSciTokensAccess.cc b/src/XrdSciTokens/XrdSciTokensAccess.cc index 30a1527b3a3..2616c72fff0 100644 --- a/src/XrdSciTokens/XrdSciTokensAccess.cc +++ b/src/XrdSciTokens/XrdSciTokensAccess.cc @@ -42,6 +42,13 @@ enum LogMask { All = 0xff }; +enum IssuerAuthz { + Capability = 0x01, + Group = 0x02, + Mapping = 0x04, + Default = 0x07 +}; + std::string LogMaskToString(int mask) { if (mask == LogMask::All) {return "all";} @@ -272,10 +279,12 @@ struct IssuerConfig const std::vector &base_paths, const std::vector &restricted_paths, bool map_subject, + uint32_t authz_strategy, const std::string &default_user, const std::string &username_claim, const std::vector rules) : m_map_subject(map_subject || !username_claim.empty()), + m_authz_strategy(authz_strategy), m_name(issuer_name), m_url(issuer_url), m_default_user(default_user), @@ -286,6 +295,7 @@ struct IssuerConfig {} const bool m_map_subject; + const uint32_t m_authz_strategy; const std::string m_name; const std::string m_url; const std::string m_default_user; @@ -337,7 +347,9 @@ class XrdAccRules { public: XrdAccRules(uint64_t expiry_time, const std::string &username, const std::string &token_subject, - const std::string &issuer, const std::vector &rules, const std::vector &groups) : + const std::string &issuer, const std::vector &rules, const std::vector &groups, + uint32_t authz_strategy) : + m_authz_strategy(authz_strategy), m_expiry_time(expiry_time), m_username(username), m_token_subject(token_subject), @@ -420,10 +432,13 @@ class XrdAccRules const std::string & get_default_username() const {return m_username;} const std::string & get_issuer() const {return m_issuer;} + uint32_t get_authz_strategy() const {return m_authz_strategy;} + size_t size() const {return m_rules.size();} const std::vector &groups() const {return m_groups;} private: + uint32_t m_authz_strategy; AccessRulesRaw m_rules; uint64_t m_expiry_time{0}; const std::string m_username; @@ -513,8 +528,9 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, std::string issuer; std::vector map_rules; std::vector groups; - if (GenerateAcls(authz, cache_expiry, rules, username, token_subject, issuer, map_rules, groups)) { - access_rules.reset(new XrdAccRules(now + cache_expiry, username, token_subject, issuer, map_rules, groups)); + uint32_t authz_strategy; + if (GenerateAcls(authz, cache_expiry, rules, username, token_subject, issuer, map_rules, groups, authz_strategy)) { + access_rules.reset(new XrdAccRules(now + cache_expiry, username, token_subject, issuer, map_rules, groups, authz_strategy)); access_rules->parse(rules); } else { m_log.Log(LogMask::Warning, "Access", "Failed to generate ACLs for token"); @@ -533,7 +549,8 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, m_log.Log(LogMask::Debug, "Access", "Cached token", access_rules->str().c_str()); } - // Strategy: we populate the name in the XrdSecEntity if: + // Strategy: assuming the corresponding strategy is enabled, we populate the name in + // the XrdSecEntity if: // 1. There are scopes present in the token that authorize the request, // 2. The token is mapped by some rule in the mapfile (group or subject-based mapping). // The default username for the issuer is only used in (1). @@ -553,7 +570,8 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, if (!issuer.empty()) { new_secentity.vorg = strdup(issuer.c_str()); } - if (access_rules->groups().size()) { + bool group_success = false; + if ((access_rules->get_authz_strategy() & IssuerAuthz::Group) && access_rules->groups().size()) { std::stringstream ss; for (const auto &grp : access_rules->groups()) { ss << grp << " "; @@ -564,6 +582,7 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, memcpy(new_secentity.grps, groups_str.c_str(), groups_str.size()); new_secentity.grps[groups_str.size()] = '\0'; } + group_success = true; } std::string username; @@ -571,15 +590,15 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, bool scope_success = false; username = access_rules->get_username(path); - mapping_success = !username.empty(); - scope_success = access_rules->apply(oper, path); + mapping_success = (access_rules->get_authz_strategy() & IssuerAuthz::Mapping) && !username.empty(); + scope_success = (access_rules->get_authz_strategy() & IssuerAuthz::Capability) && access_rules->apply(oper, path); if (scope_success && (m_log.getMsgMask() & LogMask::Debug)) { std::stringstream ss; ss << "Grant authorization based on scopes for operation=" << OpToName(oper) << ", path=" << path; m_log.Log(LogMask::Debug, "Access", ss.str().c_str()); } - if (!scope_success && !mapping_success) { + if (!scope_success && !mapping_success && !group_success) { auto returned_accs = OnMissing(&new_secentity, path, oper, env); // Clean up the new_secentity if (new_secentity.vorg != nullptr) free(new_secentity.vorg); @@ -590,11 +609,13 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, } // Default user only applies to scope-based mappings. - if (!mapping_success && scope_success) { - mapping_success = !(username = access_rules->get_default_username()).empty(); + if (scope_success && username.empty()) { + username = access_rules->get_default_username(); } - if (mapping_success) { + // Setting the request.name will pass the username to the next plugin. + // Ensure we do that only if map-based or scope-based authorization worked. + if (scope_success || mapping_success) { // Set scitokens.name in the extra attribute Entity->eaAPI->Add("request.name", username, true); new_secentity.eaAPI->Add("request.name", username, true); @@ -730,7 +751,7 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, return XrdAccPriv_None; } - bool GenerateAcls(const std::string &authz, uint64_t &cache_expiry, AccessRulesRaw &rules, std::string &username, std::string &token_subject, std::string &issuer, std::vector &map_rules, std::vector &groups) { + bool GenerateAcls(const std::string &authz, uint64_t &cache_expiry, AccessRulesRaw &rules, std::string &username, std::string &token_subject, std::string &issuer, std::vector &map_rules, std::vector &groups, uint32_t &authz_strategy) { // Does this look like a JWT? If not, bail out early and // do not pollute the log. bool looks_good = true; @@ -962,6 +983,7 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, xrd_rules.emplace_back(AOP_Delete, write_path); } } + authz_strategy = config.m_authz_strategy; pthread_rwlock_unlock(&m_config_lock); @@ -1250,10 +1272,30 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, auto map_subject = reader.GetBoolean(section, "map_subject", false); auto username_claim = reader.Get(section, "username_claim", ""); + auto authz_strategy_str = reader.Get(section, "authorization_strategy", ""); + uint32_t authz_strategy = 0; + if (authz_strategy_str.empty()) { + authz_strategy = IssuerAuthz::Default; + } else { + std::istringstream authz_strategy_stream(authz_strategy_str); + std::string authz_str; + while (std::getline(authz_strategy_stream, authz_str, ' ')) { + if (!strcasecmp(authz_str.c_str(), "capability")) { + authz_strategy |= IssuerAuthz::Capability; + } else if (!strcasecmp(authz_str.c_str(), "group")) { + authz_strategy |= IssuerAuthz::Group; + } else if (!strcasecmp(authz_str.c_str(), "mapping")) { + authz_strategy |= IssuerAuthz::Mapping; + } else { + m_log.Log(LogMask::Error, "Reconfig", "Unknown authorization strategy (ignoring):", authz_str.c_str()); + } + } + } + issuers.emplace(std::piecewise_construct, std::forward_as_tuple(issuer), std::forward_as_tuple(name, issuer, base_paths, restricted_paths, - map_subject, default_user, username_claim, rules)); + map_subject, authz_strategy, default_user, username_claim, rules)); } if (issuers.empty()) { From 659a8f273911b24993daf927f74037f1fd8d5e48 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Fri, 22 Mar 2024 10:23:14 +0100 Subject: [PATCH 046/276] [CMake] Move baseline required C++ standard to C++17 --- cmake/XRootDOSDefs.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/XRootDOSDefs.cmake b/cmake/XRootDOSDefs.cmake index 5f1ebc71546..342838ef737 100644 --- a/cmake/XRootDOSDefs.cmake +++ b/cmake/XRootDOSDefs.cmake @@ -18,7 +18,7 @@ define_default( LIBRARY_PATH_PREFIX "lib" ) #------------------------------------------------------------------------------- # Enable c++14 #------------------------------------------------------------------------------- -set(CMAKE_CXX_STANDARD 14 CACHE STRING "C++ Standard") +set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ Standard") set(CMAKE_CXX_STANDARD_REQUIRED TRUE) if( ENABLE_ASAN ) From 6102ea88296f0046057f4a72541394e9a7fe8827 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Mon, 25 Mar 2024 14:47:37 +0100 Subject: [PATCH 047/276] [Tests] Update header check to use C++17 standard --- tests/check-headers.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/check-headers.sh b/tests/check-headers.sh index eba2ddd65e6..b9cc1d1dec8 100755 --- a/tests/check-headers.sh +++ b/tests/check-headers.sh @@ -10,7 +10,7 @@ # shellcheck disable=SC2086 : "${CXX:=c++}" -: "${CXXFLAGS:=-Wall}" +: "${CXXFLAGS:=-Wall -std=c++17}" : "${INCLUDE_DIR:=${1:-/usr/include/xrootd}}" if ! command -v "${CXX}" >/dev/null; then From c95e804624fe01ac6d6cc2a46ee3cd02d1055266 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Fri, 22 Mar 2024 10:26:57 +0100 Subject: [PATCH 048/276] [XrdEc] Replace std::result_of with std::invoke_result std::result_of is deprecated in C++17 and removed in C++20. --- src/XrdEc/XrdEcThreadPool.hh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/XrdEc/XrdEcThreadPool.hh b/src/XrdEc/XrdEcThreadPool.hh index 0b77b0638cf..df65cc4157c 100644 --- a/src/XrdEc/XrdEcThreadPool.hh +++ b/src/XrdEc/XrdEcThreadPool.hh @@ -23,7 +23,9 @@ //------------------------------------------------------------------------------ #include "XrdCl/XrdClJobManager.hh" + #include +#include #ifndef SRC_XRDEC_XRDECTHREADPOOL_HH_ #define SRC_XRDEC_XRDECTHREADPOOL_HH_ @@ -157,11 +159,11 @@ namespace XrdEc //! Schedule a functional (together with its arguments) for execution //----------------------------------------------------------------------- template - inline std::future::type> + inline std::future> Execute( FUNC func, ARGs... args ) { - using RET = typename std::result_of::type; - AnyJob *job = new AnyJob( func, std::move( args )... ); + using RET = std::invoke_result_t; + auto *job = new AnyJob( func, std::move( args )... ); std::future ftr = job->GetFuture(); threadpool.QueueJob( job, nullptr ); return ftr; From ed7ed26d3c769790d2082c0507b373a2719f30ae Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Mon, 5 Feb 2024 22:16:02 +0100 Subject: [PATCH 049/276] [CI] Move GitHub Actions to actions/upload-artifact@v4 Required due to GitHub Actions migration to node 20: https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20 --- .github/workflows/DEB.yml | 24 ++++++++++++------------ .github/workflows/RPM.yml | 22 +++++++++++----------- .github/workflows/python.yml | 6 +++--- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/.github/workflows/DEB.yml b/.github/workflows/DEB.yml index a29810084a0..782faddf1b6 100644 --- a/.github/workflows/DEB.yml +++ b/.github/workflows/DEB.yml @@ -66,15 +66,15 @@ jobs: - name: Move DEBs to Artifact Directory run: | source /etc/os-release - mkdir -p DEB/${ID}/${VERSION_CODENAME} - mv ../*.* DEB/${ID}/${VERSION_CODENAME} + mkdir -p debian/${VERSION_CODENAME} + mv ../*.* debian/${VERSION_CODENAME} - name: Upload Artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: DEB - path: DEB - retention-days: 1 + name: debian-${{ matrix.version }} + path: debian + retention-days: 14 ubuntu: name: Ubuntu (22.04) @@ -110,12 +110,12 @@ jobs: - name: Move DEBs to Artifact Directory run: | source /etc/os-release - mkdir -p DEB/${ID}/${VERSION_CODENAME} - mv ../*.* DEB/${ID}/${VERSION_CODENAME} + mkdir -p ubuntu/${VERSION_CODENAME} + mv ../*.* ubuntu/${VERSION_CODENAME} - name: Upload Artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: DEB - path: DEB - retention-days: 1 + name: ubuntu-22.04 + path: ubuntu + retention-days: 14 diff --git a/.github/workflows/RPM.yml b/.github/workflows/RPM.yml index 7343780b876..70b4e7667a5 100644 --- a/.github/workflows/RPM.yml +++ b/.github/workflows/RPM.yml @@ -58,9 +58,9 @@ jobs: - name: Upload Artifacts uses: actions/upload-artifact@v3 with: - name: RPM + name: centos7 path: RPMS - retention-days: 1 + retention-days: 14 alma8: name: Alma Linux 8 @@ -101,11 +101,11 @@ jobs: run: mkdir RPMS && mv $(rpm -E '%{_rpmdir}')/ RPMS$(rpm -E '%{dist}' | tr . /) - name: Upload Artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: RPM + name: alma8 path: RPMS - retention-days: 1 + retention-days: 14 alma9: name: Alma Linux 9 @@ -146,11 +146,11 @@ jobs: run: mkdir RPMS && mv $(rpm -E '%{_rpmdir}')/ RPMS$(rpm -E '%{dist}' | tr . /) - name: Upload Artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: RPM + name: alma9 path: RPMS - retention-days: 1 + retention-days: 14 fedora: name: Fedora 39 @@ -190,8 +190,8 @@ jobs: run: mkdir RPMS && mv $(rpm -E '%{_rpmdir}')/ RPMS$(rpm -E '%{dist}' | tr . /) - name: Upload Artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: RPM + name: fc39 path: RPMS - retention-days: 1 + retention-days: 14 diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index e2bebb71b44..80324fab377 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -68,8 +68,8 @@ jobs: run: mv *.whl dist/ - name: Upload Artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: Python + name: python-${{ matrix.version }} path: dist - retention-days: 1 + retention-days: 14 From 548fdc2a153514eca099ecd49bac96fdbf74632d Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Thu, 28 Mar 2024 08:44:29 +0100 Subject: [PATCH 050/276] [XrdSciTokens] Fix application of access rules when base path is '/' The comparison path[rule.second.size()] == '/' for ensuring path is a subdirectory of the rule's path does not work for '/'. For example if the rule's path is '/' and requested path is '/tmp', then the comparison above checks path[rule.second.size()] == path[1] == '/', but actually, path[1] == 't'. --- src/XrdSciTokens/XrdSciTokensAccess.cc | 50 +++++++++++++++----------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/src/XrdSciTokens/XrdSciTokensAccess.cc b/src/XrdSciTokens/XrdSciTokensAccess.cc index 2616c72fff0..0428b5cc0fe 100644 --- a/src/XrdSciTokens/XrdSciTokensAccess.cc +++ b/src/XrdSciTokens/XrdSciTokensAccess.cc @@ -361,28 +361,36 @@ class XrdAccRules ~XrdAccRules() {} bool apply(Access_Operation oper, std::string path) { - for (const auto & rule : m_rules) { - // The rule permits if both conditions are met: - // - The operation type matches the requested operation, - // - The requested path is a substring of the ACL's permitted path, AND - // - Either the requested path and ACL path is the same OR the requested path is a subdir of the ACL path. - // - // The third rule implies if the rule permits read:/foo, we should NOT authorize read:/foobar. - if ((oper == rule.first) && - !path.compare(0, rule.second.size(), rule.second, 0, rule.second.size()) && - (rule.second.size() == path.length() || path[rule.second.size()]=='/')) - { - return true; - } - // according to WLCG token specs, allow creation of required superfolders for a new file if requested - if ((oper == rule.first) && (oper == AOP_Stat || oper == AOP_Mkdir) - && rule.second.size() >= path.length() - && !rule.second.compare(0, path.size(), path, 0, path.size()) - && (rule.second.size() == path.length() || rule.second[path.length()] == '/')) { - return true; - } + auto is_subdirectory = [](const std::string& dir, const std::string& subdir) { + if (subdir.size() < dir.size()) + return false; + + if (subdir.compare(0, dir.size(), dir, 0, dir.size()) != 0) + return false; + + return dir.size() == subdir.size() || subdir[dir.size()] == '/' || dir == "/"; + }; + + for (const auto & rule : m_rules) { + // Skip rules that don't match the current operation + if (rule.first != oper) + continue; + + // If the rule allows any path, allow the operation + if (rule.second == "/") + return true; + + // Allow operation if path is a subdirectory of the rule's path + if (is_subdirectory(rule.second, path)) { + return true; + } else { + // Allow stat and mkdir of parent directories to comply with WLCG token specs + if (oper == AOP_Stat || oper == AOP_Mkdir) + if (is_subdirectory(path, rule.second)) + return true; } - return false; + } + return false; } bool expired() const {return monotonic_time() > m_expiry_time;} From bae878fe89d168ce99b687056b34db7a66e131c2 Mon Sep 17 00:00:00 2001 From: Mattias Ellert Date: Mon, 25 Mar 2024 20:36:01 +0100 Subject: [PATCH 051/276] Minor test fixes --- tests/XrdCl/XrdClFileCopyTest.cc | 4 ++-- tests/XrdCl/XrdClPoller.cc | 7 +++++-- tests/XrdCl/XrdClSocket.cc | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/XrdCl/XrdClFileCopyTest.cc b/tests/XrdCl/XrdClFileCopyTest.cc index d991b358e15..deb452495ac 100644 --- a/tests/XrdCl/XrdClFileCopyTest.cc +++ b/tests/XrdCl/XrdClFileCopyTest.cc @@ -616,8 +616,8 @@ void FileCopyTest::CopyTestFunc( bool thirdParty ) // Copy from a non-existent source //---------------------------------------------------------------------------- results.Clear(); - properties.Set( "source", "root://localhost:9999//test" ); - properties.Set( "target", targetURL ); + properties.Set( "source", "root://localhost:9997//test" ); // was 9999, this change allows for + properties.Set( "target", targetURL ); // parallel testing properties.Set( "initTimeout", 10 ); properties.Set( "thirdParty", "only" ); GTEST_ASSERT_XRDST( process3.AddJob( properties, &results ) ); diff --git a/tests/XrdCl/XrdClPoller.cc b/tests/XrdCl/XrdClPoller.cc index 5569c64799b..d308741701a 100644 --- a/tests/XrdCl/XrdClPoller.cc +++ b/tests/XrdCl/XrdClPoller.cc @@ -216,7 +216,10 @@ TEST(PollerTest, FunctionTest) //---------------------------------------------------------------------------- Server server( Server::Both ); Socket s[3]; - EXPECT_TRUE( server.Setup( 9999, 3, new RandomPumpHandlerFactory() ) ); + uint16_t port = 9996; // was 9999, but we need to change ports from other + // tests so that we can run all of them in parallel. + // Will find another, better way to ensure this in the future + EXPECT_TRUE( server.Setup( port, 3, new RandomPumpHandlerFactory() ) ); EXPECT_TRUE( server.Start() ); EXPECT_TRUE( poller->Initialize() ); EXPECT_TRUE( poller->Start() ); @@ -228,7 +231,7 @@ TEST(PollerTest, FunctionTest) for( int i = 0; i < 3; ++i ) { GTEST_ASSERT_XRDST( s[i].Initialize() ); - GTEST_ASSERT_XRDST( s[i].Connect( "localhost", 9999 ) ); + GTEST_ASSERT_XRDST( s[i].Connect( "localhost", port ) ); EXPECT_TRUE( poller->AddSocket( &s[i], handler ) ); EXPECT_TRUE( poller->EnableReadNotification( &s[i], true, 60 ) ); EXPECT_TRUE( poller->IsRegistered( &s[i] ) ); diff --git a/tests/XrdCl/XrdClSocket.cc b/tests/XrdCl/XrdClSocket.cc index df8f433bff5..c2e42a424f0 100644 --- a/tests/XrdCl/XrdClSocket.cc +++ b/tests/XrdCl/XrdClSocket.cc @@ -223,8 +223,8 @@ TEST(SocketTest, TransferTest) // Start up the server and connect to it //---------------------------------------------------------------------------- uint16_t port = 9998; // was 9999, but we need to change ports from other - // tests so that we can run all of them in parallel. - // Will find another, better way to ensure this in the future + // tests so that we can run all of them in parallel. + // Will find another, better way to ensure this in the future EXPECT_TRUE( serv.Setup( port, 1, new RandomHandlerFactory() ) ); EXPECT_TRUE( serv.Start() ); From 15ae0dc89152ec492f971687c07b8ecea1eceba1 Mon Sep 17 00:00:00 2001 From: Simon Thiele Date: Mon, 22 Jan 2024 16:05:30 +0100 Subject: [PATCH 052/276] [XrdSciTokens] Implement ability to have token groups as a separate claim --- src/XrdSciTokens/XrdSciTokensAccess.cc | 36 ++++++++++++++++---------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/XrdSciTokens/XrdSciTokensAccess.cc b/src/XrdSciTokens/XrdSciTokensAccess.cc index 0428b5cc0fe..b7e69c1fc19 100644 --- a/src/XrdSciTokens/XrdSciTokensAccess.cc +++ b/src/XrdSciTokens/XrdSciTokensAccess.cc @@ -282,6 +282,7 @@ struct IssuerConfig uint32_t authz_strategy, const std::string &default_user, const std::string &username_claim, + const std::string &groups_claim, const std::vector rules) : m_map_subject(map_subject || !username_claim.empty()), m_authz_strategy(authz_strategy), @@ -289,6 +290,7 @@ struct IssuerConfig m_url(issuer_url), m_default_user(default_user), m_username_claim(username_claim), + m_groups_claim(groups_claim), m_base_paths(base_paths), m_restricted_paths(restricted_paths), m_map_rules(rules) @@ -300,6 +302,7 @@ struct IssuerConfig const std::string m_url; const std::string m_default_user; const std::string m_username_claim; + const std::string m_groups_claim; const std::vector m_base_paths; const std::vector m_restricted_paths; const std::vector m_map_rules; @@ -842,9 +845,26 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, } enforcer_destroy(enf); + pthread_rwlock_rdlock(&m_config_lock); + auto iter = m_issuers.find(token_issuer); + if (iter == m_issuers.end()) { + pthread_rwlock_unlock(&m_config_lock); + m_log.Log(LogMask::Warning, "GenerateAcls", "Authorized issuer without a config."); + scitoken_destroy(token); + return false; + } + const auto &config = iter->second; + value = nullptr; + char **group_list; std::vector groups_parsed; - if (!scitoken_get_claim_string_list(token, "wlcg.groups", &group_list, &err_msg)) { + const char* tmp_group_claim; + if (!config.m_groups_claim.empty()) { + tmp_group_claim = config.m_groups_claim.c_str(); + } else { + tmp_group_claim = "wlcg.groups"; + } + if (!scitoken_get_claim_string_list(token, tmp_group_claim, &group_list, &err_msg)) { for (int idx=0; group_list[idx]; idx++) { groups_parsed.emplace_back(group_list[idx]); } @@ -855,17 +875,6 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, free(err_msg); } - - pthread_rwlock_rdlock(&m_config_lock); - auto iter = m_issuers.find(token_issuer); - if (iter == m_issuers.end()) { - pthread_rwlock_unlock(&m_config_lock); - m_log.Log(LogMask::Warning, "GenerateAcls", "Authorized issuer without a config."); - scitoken_destroy(token); - return false; - } - const auto &config = iter->second; - value = nullptr; if (scitoken_get_claim_string(token, "sub", &value, &err_msg)) { pthread_rwlock_unlock(&m_config_lock); m_log.Log(LogMask::Warning, "GenerateAcls", "Failed to get token subject:", err_msg); @@ -1279,6 +1288,7 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, auto default_user = reader.Get(section, "default_user", ""); auto map_subject = reader.GetBoolean(section, "map_subject", false); auto username_claim = reader.Get(section, "username_claim", ""); + auto groups_claim = reader.Get(section, "groups_claim", ""); auto authz_strategy_str = reader.Get(section, "authorization_strategy", ""); uint32_t authz_strategy = 0; @@ -1303,7 +1313,7 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, issuers.emplace(std::piecewise_construct, std::forward_as_tuple(issuer), std::forward_as_tuple(name, issuer, base_paths, restricted_paths, - map_subject, authz_strategy, default_user, username_claim, rules)); + map_subject, authz_strategy, default_user, username_claim, groups_claim, rules)); } if (issuers.empty()) { From 42c260656372824c475a53baf424270cd53a197c Mon Sep 17 00:00:00 2001 From: Simon Thiele Date: Thu, 25 Jan 2024 10:01:18 +0100 Subject: [PATCH 053/276] [XrdSciTokens] Update README.md about newly introduced groups_claim --- src/XrdSciTokens/README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/XrdSciTokens/README.md b/src/XrdSciTokens/README.md index b7c69fdbb04..1edecf94486 100644 --- a/src/XrdSciTokens/README.md +++ b/src/XrdSciTokens/README.md @@ -115,7 +115,9 @@ are: - `username_claim` (optional): Not all issuers put the desired username in the `sub` claim (sometimes the subject is set to a de-identified value). To use an alternate claim as the username, such as `uid`, set this to the desired claim name. If set, it overrides `map_subject` and `default_user`. - - `name_mapfile` (options): If set, then the referenced file is parsed as a JSON object and the specified mappings + - `groups_claim` (optional): Not all issuers put the desired groups in the `wlcg.groups` claim. To use an alternate claim + as the groups, set this to the desired claim name. If not set, the default is `wlcg.groups`. + - `name_mapfile` (options): If set, then the referenced file is parsed as a JSON object and the specified mappings are applied to the username inside the XRootD framework. See below for more information on the mapfile. - `authorization_strategy` (optional): One or more authorizations to use from the token. Multiple (space separated) items may be specified from the following valid values: @@ -168,7 +170,8 @@ The enumerated keys are: For example, if the issuer's base path is `/home`, the operation is accessing `/home/bbockelm/foo`, and the path in the rule is `/bbockelm`, then this attribute evaluates to `true`. Note the path value and the requested path must be normalized; if presented with `/home//bbockelm/`, then this is treated as if `/home/bbockelm` was given. - - `group`: Case-sensitive match against one of the groups in the token. + - `group`: Case-sensitive match against one of the groups in the token (the claim specifying the groups is configurable, controlled by the + `groups_claim` variable in the issuer config; default is `wlcg.groups`). - `ignore`: If present (regardless of the value), the rule is ignored. - `comment`: Ignored; reserved for adding comments from the administrator. From a153f8dab92ed3bfda0c68283a54bc555faab87d Mon Sep 17 00:00:00 2001 From: Simon Thiele Date: Mon, 11 Mar 2024 15:19:50 +0100 Subject: [PATCH 054/276] [XrdSciTokens] Use a copy of config in GenerateAcls instead of locking --- src/XrdSciTokens/XrdSciTokensAccess.cc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/XrdSciTokens/XrdSciTokensAccess.cc b/src/XrdSciTokens/XrdSciTokensAccess.cc index b7e69c1fc19..7cd16173d9f 100644 --- a/src/XrdSciTokens/XrdSciTokensAccess.cc +++ b/src/XrdSciTokens/XrdSciTokensAccess.cc @@ -853,7 +853,8 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, scitoken_destroy(token); return false; } - const auto &config = iter->second; + const auto config = iter->second; + pthread_rwlock_unlock(&m_config_lock); value = nullptr; char **group_list; @@ -876,7 +877,6 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, } if (scitoken_get_claim_string(token, "sub", &value, &err_msg)) { - pthread_rwlock_unlock(&m_config_lock); m_log.Log(LogMask::Warning, "GenerateAcls", "Failed to get token subject:", err_msg); free(err_msg); scitoken_destroy(token); @@ -888,7 +888,6 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, auto tmp_username = token_subject; if (!config.m_username_claim.empty()) { if (scitoken_get_claim_string(token, config.m_username_claim.c_str(), &value, &err_msg)) { - pthread_rwlock_unlock(&m_config_lock); m_log.Log(LogMask::Warning, "GenerateAcls", "Failed to get token username:", err_msg); free(err_msg); scitoken_destroy(token); @@ -1002,8 +1001,6 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, } authz_strategy = config.m_authz_strategy; - pthread_rwlock_unlock(&m_config_lock); - cache_expiry = expiry; rules = std::move(xrd_rules); username = std::move(tmp_username); From 027a51e897fc918d62f7265ff20898e14d06f2d3 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Wed, 3 Apr 2024 11:57:39 +0200 Subject: [PATCH 055/276] [XrdSciTokens] Fix indentation of local variables in try block --- src/XrdSciTokens/XrdSciTokensAccess.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/XrdSciTokens/XrdSciTokensAccess.cc b/src/XrdSciTokens/XrdSciTokensAccess.cc index 7cd16173d9f..c85a22ff6bc 100644 --- a/src/XrdSciTokens/XrdSciTokensAccess.cc +++ b/src/XrdSciTokens/XrdSciTokensAccess.cc @@ -532,8 +532,8 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, if (!access_rules) { m_log.Log(LogMask::Debug, "Access", "Token not found in recent cache; parsing."); try { - uint64_t cache_expiry; - AccessRulesRaw rules; + uint64_t cache_expiry; + AccessRulesRaw rules; std::string username; std::string token_subject; std::string issuer; From 9a2e8b99d95b8bdbe64e1cf9aaf29e9feab27a90 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 2 Apr 2024 15:46:52 +0200 Subject: [PATCH 056/276] [XrdSciTokens] Simplify handling of groups claim --- src/XrdSciTokens/XrdSciTokensAccess.cc | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/XrdSciTokens/XrdSciTokensAccess.cc b/src/XrdSciTokens/XrdSciTokensAccess.cc index c85a22ff6bc..13e26b3f44e 100644 --- a/src/XrdSciTokens/XrdSciTokensAccess.cc +++ b/src/XrdSciTokens/XrdSciTokensAccess.cc @@ -859,13 +859,7 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, char **group_list; std::vector groups_parsed; - const char* tmp_group_claim; - if (!config.m_groups_claim.empty()) { - tmp_group_claim = config.m_groups_claim.c_str(); - } else { - tmp_group_claim = "wlcg.groups"; - } - if (!scitoken_get_claim_string_list(token, tmp_group_claim, &group_list, &err_msg)) { + if (scitoken_get_claim_string_list(token, config.m_groups_claim.c_str(), &group_list, &err_msg) == 0) { for (int idx=0; group_list[idx]; idx++) { groups_parsed.emplace_back(group_list[idx]); } @@ -1285,7 +1279,7 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, auto default_user = reader.Get(section, "default_user", ""); auto map_subject = reader.GetBoolean(section, "map_subject", false); auto username_claim = reader.Get(section, "username_claim", ""); - auto groups_claim = reader.Get(section, "groups_claim", ""); + auto groups_claim = reader.Get(section, "groups_claim", "wlcg.groups"); auto authz_strategy_str = reader.Get(section, "authorization_strategy", ""); uint32_t authz_strategy = 0; From b877013012684f1c7536b9c138dbdd80719c07e6 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Wed, 3 Apr 2024 14:50:55 +0200 Subject: [PATCH 057/276] [XrdSciTokens] Warn if something goes wrong when parsing token groups --- src/XrdSciTokens/XrdSciTokensAccess.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/XrdSciTokens/XrdSciTokensAccess.cc b/src/XrdSciTokens/XrdSciTokensAccess.cc index 13e26b3f44e..944149a0d2a 100644 --- a/src/XrdSciTokens/XrdSciTokensAccess.cc +++ b/src/XrdSciTokens/XrdSciTokensAccess.cc @@ -865,8 +865,8 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper, } scitoken_free_string_list(group_list); } else { - // For now, we silently ignore errors. - // std::cerr << "Failed to get groups: " << err_msg << std::endl; + // Failing to parse groups is not fatal, but we should still warn about what's wrong + m_log.Log(LogMask::Warning, "GenerateAcls", "Failed to get token groups:", err_msg); free(err_msg); } From 4a7cb0f9786c461d2b734ebb6b10071ec3d096c4 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Wed, 27 Mar 2024 14:52:29 +0100 Subject: [PATCH 058/276] [CMake] Conditionally append private include directory ROOT only needs XRootD's public headers, and private headers come from a separate RPM. This allows building projects against XRootD that don't need private headers by only installing the necessary public header RPMs. Co-authored-by: Stephan Hageboeck --- cmake/XRootDConfig.cmake.in | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmake/XRootDConfig.cmake.in b/cmake/XRootDConfig.cmake.in index 0a4ce3fe557..fc630f09520 100644 --- a/cmake/XRootDConfig.cmake.in +++ b/cmake/XRootDConfig.cmake.in @@ -53,7 +53,11 @@ set_and_check(XRootD_DATA_DIR "@PACKAGE_CMAKE_INSTALL_DATADIR@") set_and_check(XRootD_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@/xrootd") set_and_check(XRootD_LIB_DIR "@PACKAGE_CMAKE_INSTALL_LIBDIR@") -set(XRootD_INCLUDE_DIRS "${XRootD_INCLUDE_DIR};${XRootD_INCLUDE_DIR}/private") +set(XRootD_INCLUDE_DIRS "${XRootD_INCLUDE_DIR}") +if(IS_DIRECTORY "${XRootD_INCLUDE_DIR}/private") + list(APPEND XRootD_INCLUDE_DIRS "${XRootD_INCLUDE_DIR}/private") +endif() + set(XROOTD_INCLUDE_DIRS "${XRootD_INCLUDE_DIRS}") # backward compatibility ################################################################################ From aab1824a7cc3ce98c34ce1fd02319ff594802181 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 9 Apr 2024 16:34:09 +0200 Subject: [PATCH 059/276] [XrdCl] Simplify implementation of FileStateHandler::IsRecoverable Loop over a list of recoverable errors and return false without looping if recovering is disabled for both read and write operations. --- src/XrdCl/XrdClFileStateHandler.cc | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/XrdCl/XrdClFileStateHandler.cc b/src/XrdCl/XrdClFileStateHandler.cc index d9491c85c3c..371869f47b2 100644 --- a/src/XrdCl/XrdClFileStateHandler.cc +++ b/src/XrdCl/XrdClFileStateHandler.cc @@ -2879,18 +2879,19 @@ namespace XrdCl //---------------------------------------------------------------------------- bool FileStateHandler::IsRecoverable( const XRootDStatus &status ) const { - if( status.code == errSocketError || status.code == errInvalidSession || - status.code == errTlsError || status.code == errSocketTimeout || - status.code == errOperationInterrupted) - { - if( IsReadOnly() && !pDoRecoverRead ) - return false; + const auto recoverable_errors = { + errSocketError, + errSocketTimeout, + errInvalidSession, + errTlsError, + errOperationInterrupted + }; - if( !IsReadOnly() && !pDoRecoverWrite ) - return false; + if (pDoRecoverRead || pDoRecoverWrite) + for (const auto error : recoverable_errors) + if (status.code == error) + return IsReadOnly() ? pDoRecoverRead : pDoRecoverWrite; - return true; - } return false; } From 36e5e9f9e97cc06ba0e0be7eac1785148199d627 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 9 Apr 2024 16:38:14 +0200 Subject: [PATCH 060/276] [XrdCl] Add errInternal to list of recoverable errors Fixes: #2210 --- src/XrdCl/XrdClFileStateHandler.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/XrdCl/XrdClFileStateHandler.cc b/src/XrdCl/XrdClFileStateHandler.cc index 371869f47b2..49873c27008 100644 --- a/src/XrdCl/XrdClFileStateHandler.cc +++ b/src/XrdCl/XrdClFileStateHandler.cc @@ -2883,6 +2883,7 @@ namespace XrdCl errSocketError, errSocketTimeout, errInvalidSession, + errInternal, errTlsError, errOperationInterrupted }; From 6861a86e440585c432f8c3a4a5f464f86a9c1a1a Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Wed, 10 Apr 2024 10:50:06 +0200 Subject: [PATCH 061/276] [XrdMacaroons] Support negative directives in macaroons.trace option Fixes: #2224 --- src/XrdMacaroons/XrdMacaroonsConfigure.cc | 83 +++++++++++------------ 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/src/XrdMacaroons/XrdMacaroonsConfigure.cc b/src/XrdMacaroons/XrdMacaroonsConfigure.cc index 299d005f4b1..66c2dbd1b3e 100644 --- a/src/XrdMacaroons/XrdMacaroonsConfigure.cc +++ b/src/XrdMacaroons/XrdMacaroonsConfigure.cc @@ -97,53 +97,52 @@ bool Handler::Config(const char *config, XrdOucEnv *env, XrdSysError *log, } -bool Handler::xtrace(XrdOucStream &config_obj, XrdSysError *log) +bool Handler::xtrace(XrdOucStream &Config, XrdSysError *log) { - char *val = config_obj.GetWord(); - if (!val || !val[0]) - { - log->Emsg("Config", "macaroons.trace requires at least one directive [all | error | warning | info | debug | none]"); - return false; - } - // If the config option is given, reset the log mask. - log->setMsgMask(0); + static struct traceopts { const char *opname; enum LogMask opval; } tropts[] = { + { "all", LogMask::All }, + { "error", LogMask::Error }, + { "warning", LogMask::Warning }, + { "info", LogMask::Info }, + { "debug", LogMask::Debug } + }; - do { - if (!strcmp(val, "all")) - { - log->setMsgMask(log->getMsgMask() | LogMask::All); - } - else if (!strcmp(val, "error")) - { - log->setMsgMask(log->getMsgMask() | LogMask::Error); - } - else if (!strcmp(val, "warning")) - { - log->setMsgMask(log->getMsgMask() | LogMask::Warning); - } - else if (!strcmp(val, "info")) - { - log->setMsgMask(log->getMsgMask() | LogMask::Info); - } - else if (!strcmp(val, "debug")) - { - log->setMsgMask(log->getMsgMask() | LogMask::Debug); - } - else if (!strcmp(val, "none")) - { - log->setMsgMask(0); - } - else - { - log->Emsg("Config", "macaroons.trace encountered an unknown directive:", val); - return false; + int i, neg, trval = 0, numopts = sizeof(tropts)/sizeof(struct traceopts); + + char *val = Config.GetWord(); + + if (!val || !*val) { + log->Emsg("Config", "macaroons.trace requires at least one directive" + " [ all | error | warning | info | debug | none | off ]"); + return false; + } + + while (val && *val) { + if (strcmp(val, "off") == 0 || strcmp(val, "none") == 0) { + trval = 0; + } else { + if ((neg = (val[0] == '-' && val[1]))) + val++; + for (i = 0; i < numopts; i++) { + if (!strcmp(val, tropts[i].opname)) { + if (neg) + trval &= ~tropts[i].opval; + else + trval |= tropts[i].opval; + break; } - val = config_obj.GetWord(); - } while (val); + } + if (neg) --val; + if (i >= numopts) + log->Emsg("Config", "macaroons.trace: ignoring invalid trace option:", val); + } + val = Config.GetWord(); + } - return true; -} + log->setMsgMask(trval); + return true; +} bool Handler::xmaxduration(XrdOucStream &config_obj, XrdSysError *log, ssize_t &max_duration) { From bb2503cce68e2abf184ee2a96820e811a2e4ac60 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Wed, 10 Apr 2024 17:36:12 +0200 Subject: [PATCH 062/276] [XrdSecgsi] Fail CA check when prococol.gsi -ca:verify is set Issue: #2150 --- src/XrdSecgsi/XrdSecProtocolgsi.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/XrdSecgsi/XrdSecProtocolgsi.cc b/src/XrdSecgsi/XrdSecProtocolgsi.cc index ff067e9775f..699afb8112c 100644 --- a/src/XrdSecgsi/XrdSecProtocolgsi.cc +++ b/src/XrdSecgsi/XrdSecProtocolgsi.cc @@ -4591,8 +4591,9 @@ bool XrdSecProtocolgsi::VerifyCA(int opt, X509Chain *cca, XrdCryptoFactory *CF) } } else { if (CACheck > caNoVerify) { - // Check self-signature - if (!(verified = cca->CheckCA())) + // Check self-signature and fail if needed + bool checkselfsigned = (CACheck > caVerifyss) ? true : false; + if (!(verified = cca->CheckCA(checkselfsigned))) PRINT("CA certificate self-signed: integrity check failed ("<SubjectHash()<<")"); } else { // Set OK in any case From 937ec8d9bbec479d6a5581bc89f3da9fa0eda2f6 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Thu, 11 Apr 2024 09:41:42 +0200 Subject: [PATCH 063/276] [Docs] Add XRootD icon and logos to use with doxygen --- docs/images/xrootd-icon-big.png | Bin 0 -> 62320 bytes docs/images/xrootd-icon.png | Bin 0 -> 9615 bytes docs/images/xrootd-logo.png | Bin 0 -> 92337 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/images/xrootd-icon-big.png create mode 100644 docs/images/xrootd-icon.png create mode 100644 docs/images/xrootd-logo.png diff --git a/docs/images/xrootd-icon-big.png b/docs/images/xrootd-icon-big.png new file mode 100644 index 0000000000000000000000000000000000000000..421dbe6b9e07682e14ed74ea868e740c30b013cb GIT binary patch literal 62320 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 literal 0 HcmV?d00001 From b7a1597a10130c8cf66593a4cf68045ca0d99030 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Thu, 11 Apr 2024 09:42:55 +0200 Subject: [PATCH 064/276] [Docs] Update doxygen configuration --- Doxyfile | 2748 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 2666 insertions(+), 82 deletions(-) diff --git a/Doxyfile b/Doxyfile index ccb72e4b074..a85babb7c04 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1,213 +1,2797 @@ -# Doxyfile 1.3.7 +# Doxyfile 1.10.0 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- -PROJECT_NAME = xrootd -PROJECT_NUMBER = + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = XRootD + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = docs/images/xrootd-icon.png + +# With the PROJECT_ICON tag one can specify an icon that is included in the tabs +# when the HTML document is shown. Doxygen will copy the logo to the output +# directory. + +PROJECT_ICON = docs/images/xrootd-icon.png + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + OUTPUT_DIRECTORY = doxydoc -CREATE_SUBDIRS = NO + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. +# The default value is: NO. + +CREATE_SUBDIRS = YES + +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. +# The default value is: English. + OUTPUT_LANGUAGE = English -USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + REPEAT_BRIEF = YES -ABBREVIATE_BRIEF = + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + FULL_PATH_NAMES = NO -STRIP_FROM_PATH = -STRIP_FROM_INC_PATH = + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = src/ + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + JAVADOC_AUTOBRIEF = NO + +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + MULTILINE_CPP_IS_BRIEF = NO -DETAILS_AT_TOP = NO + +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + INHERIT_DOCS = YES -DISTRIBUTE_GROUP_DOC = NO + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + TAB_SIZE = 8 -ALIASES = + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:^^" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. + +EXTENSION_MAPPING = icc=C++ + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0 and GITHUB use the lower case version of title +# with any whitespace replaced by '-' and punctuation characters removed. +# The default value is: DOXYGEN. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_ID_STYLE = GITHUB + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = YES + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = YES + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = YES + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 2 + +# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 0 + +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. + +TIMESTAMP = NO + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + EXTRACT_ALL = YES -EXTRACT_PRIVATE = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = YES + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. +# The default value is: NO. + HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. +# The default value is: NO. + HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + INTERNAL_DOCS = NO + +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. + CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + SORT_MEMBER_DOCS = YES -SORT_BRIEF_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = YES + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = YES + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + SORT_BY_SCOPE_NAME = NO -GENERATE_TODOLIST = YES -GENERATE_TESTLIST = YES -GENERATE_BUGLIST = YES + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = NO + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = NO + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = NO + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + GENERATE_DEPRECATEDLIST= YES -ENABLED_SECTIONS = + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + #--------------------------------------------------------------------------- -# configuration options related to warning and progress messages +# Configuration options related to warning and progress messages #--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. +# The default value is: YES. + WARN_IF_DOC_ERROR = YES + +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about +# undocumented enumeration values. If set to NO, doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT +# The default value is: $file:$line: $text. + WARN_FORMAT = "$file:$line: $text" -WARN_LOGFILE = + +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). + +WARN_LOGFILE = + #--------------------------------------------------------------------------- -# configuration options related to the input files +# Configuration options related to the input files #--------------------------------------------------------------------------- -INPUT = src/ -FILE_PATTERNS = *.hh +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = README.md docs src + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding +# "INPUT_ENCODING" for further information on supported encodings. + +INPUT_FILE_ENCODING = + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, +# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, +# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, +# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to +# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.md, *.cc, *.icc, *.hh, *.py + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + RECURSIVE = YES -EXCLUDE = + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = src/XrdHttp/README-CKSUM.md \ + src/XrdOuc/XrdOucJson.hh \ + src/XrdSciTokens/vendor \ + src/XrdXml/tinyxml + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + EXCLUDE_SYMLINKS = NO -EXCLUDE_PATTERNS = -EXAMPLE_PATH = -EXAMPLE_PATTERNS = + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = */test/* */tests/* + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# ANamespace::AClass, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + EXAMPLE_RECURSIVE = NO -IMAGE_PATH = -INPUT_FILTER = + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = ./README.md + +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + #--------------------------------------------------------------------------- -# configuration options related to source browsing +# Configuration options related to source browsing #--------------------------------------------------------------------------- -SOURCE_BROWSER = NO -INLINE_SOURCES = NO + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# multi-line macros, enums or list initialized variables directly into the +# documentation. +# The default value is: NO. + +INLINE_SOURCES = YES + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + VERBATIM_HEADERS = YES + #--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index +# Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + ALPHABETICAL_INDEX = NO -COLS_IN_ALPHA_INDEX = 5 -IGNORE_PREFIX = + +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + #--------------------------------------------------------------------------- -# configuration options related to the HTML output +# Configuration options related to the HTML output #--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + HTML_FILE_EXTENSION = .html -HTML_HEADER = -HTML_FOOTER = -HTML_STYLESHEET = -HTML_ALIGN_MEMBERS = YES + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generate light mode output, DARK always +# generate dark mode output, AUTO_LIGHT automatically set the mode according to +# the user preference, use light mode if no preference is set (the default), +# AUTO_DARK automatically set the mode according to the user preference, use +# dark mode if no preference is set and TOGGLE allow to user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a color-wheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 210 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use gray-scales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = YES + +# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be +# dynamically folded and expanded in the generated HTML source code. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_CODE_FOLDING = YES + +# If the HTML_COPY_CLIPBOARD tag is set to YES then doxygen will show an icon in +# the top right corner of code and text fragments that allows the user to copy +# its content to the clipboard. Note this only works if supported by the browser +# and the web page is served via a secure context (see: +# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file: +# protocol. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COPY_CLIPBOARD = YES + +# Doxygen stores a couple of settings persistently in the browser (via e.g. +# cookies). By default these settings apply to all HTML pages generated by +# doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store +# the settings under a project specific key, such that the user preferences will +# be stored separately. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_PROJECT_COOKIE = + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + GENERATE_HTMLHELP = NO -CHM_FILE = -HHC_LOCATION = + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + TOC_EXPAND = NO + +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + DISABLE_INDEX = NO -ENUM_VALUES_PER_LINE = 4 -GENERATE_TREEVIEW = NO -TREEVIEW_WIDTH = 250 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 1 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 300 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /

Listing of: "; + stringresp += estr; + stringresp += "

\n"; + + free(estr); + + stringresp += "
"; + + stringresp += "\n" + "\n" + "" + "" + "" + "" + "" + "\n"; + } + + // Now parse the answer building the entries vector + if (iovN > 0) { + char *startp = (char *) iovP[0].iov_base, *endp = 0; + char entry[1024]; + DirListInfo e; + while ( (size_t)(startp - (char *) iovP[0].iov_base) < (size_t)( iovP[0].iov_len - 1) ) { + // Find the filename, it comes before the \n + if ((endp = (char *) strchr((const char*) startp, '\n'))) { + strncpy(entry, (char *) startp, endp - startp); + entry[endp - startp] = 0; + e.path = entry; + + endp++; + + // Now parse the stat info + TRACEI(REQ, "Dirlist " << resource.c_str() << " entry=" << entry + << " stat=" << endp); + + long dummyl; + sscanf(endp, "%ld %lld %ld %ld", + &dummyl, + &e.size, + &e.flags, + &e.modtime); + } else + strcpy(entry, (char *) startp); + + if (e.path.length() && (e.path != ".") && (e.path != "..")) { + // The entry is filled. + std::string p = "" + ""; + p += "" + "" + "" + ""; + + stringresp += p; + } + + if (endp) { + char *pp = (char *)strchr((const char *)endp, '\n'); + if (pp) startp = pp+1; + else break; + } else break; + + } + } + + // If this was the last bunch of entries, send the buffer and empty it immediately + if (final_) { + stringresp += "
ModeFlagsSizeModifiedName
file1.txt
"; + + if (e.flags & kXR_isDir) p += "d"; + else p += "-"; + + if (e.flags & kXR_other) p += "o"; + else p += "-"; + + if (e.flags & kXR_offline) p += "O"; + else p += "-"; + + if (e.flags & kXR_readable) p += "r"; + else p += "-"; + + if (e.flags & kXR_writable) p += "w"; + else p += "-"; + + if (e.flags & kXR_xset) p += "x"; + else p += "-"; + + p += "" + itos(e.flags) + "" + itos(e.size) + "" + ISOdatetime(e.modtime) + "" + ""; + p += e.path; + + free(estr); + + p += "



" + "

Request by "; + + if (prot->SecEntity.name) + stringresp += prot->SecEntity.name; + else + stringresp += prot->Link->ID; + + if (prot->SecEntity.vorg || + prot->SecEntity.name || + prot->SecEntity.moninfo || + prot->SecEntity.role) + stringresp += " ("; + + if (prot->SecEntity.vorg) { + stringresp += " VO: "; + stringresp += prot->SecEntity.vorg; + } + + if (prot->SecEntity.moninfo) { + stringresp += " DN: "; + stringresp += prot->SecEntity.moninfo; + } else + if (prot->SecEntity.name) { + stringresp += " DN: "; + stringresp += prot->SecEntity.name; + } + + if (prot->SecEntity.role) { + stringresp += " Role: "; + stringresp += prot->SecEntity.role; + if (prot->SecEntity.endorsements) { + stringresp += " ("; + stringresp += prot->SecEntity.endorsements; + stringresp += ") "; + } + } + + if (prot->SecEntity.vorg || + prot->SecEntity.moninfo || + prot->SecEntity.role) + stringresp += " )"; + + if (prot->SecEntity.host) { + stringresp += " ( "; + stringresp += prot->SecEntity.host; + stringresp += " )"; + } + + stringresp += "

\n"; + stringresp += "

Powered by XrdHTTP "; + stringresp += XrdVSTRING; + stringresp += " (CERN IT-SDC)

\n"; + + prot->SendSimpleResp(200, NULL, NULL, (char *) stringresp.c_str(), 0, keepalive); + stringresp.clear(); + return keepalive ? 1 : -1; + } + + return 0; +} + +int +XrdHttpReq::ReturnGetHeaders() { + std::string responseHeader; + if (!m_digest_header.empty()) { + responseHeader = m_digest_header; + } + long one; + if (filemodtime && XrdOucEnv::Import("XRDPFC", one)) { + if (!responseHeader.empty()) { + responseHeader += "\r\n"; + } + long object_age = time(NULL) - filemodtime; + responseHeader += std::string("Age: ") + std::to_string(object_age < 0 ? 0 : object_age); + } + + const XrdHttpReadRangeHandler::UserRangeList &uranges = readRangeHandler.ListResolvedRanges(); + if (uranges.empty() && readRangeHandler.getError()) { + prot->SendSimpleResp(readRangeHandler.getError().httpRetCode, NULL, NULL, readRangeHandler.getError().errMsg.c_str(),0,false); + return -1; + } + + if (readRangeHandler.isFullFile()) { + // Full file. + TRACEI(REQ, "Sending full file: " << filesize); + if (m_transfer_encoding_chunked && m_trailer_headers) { + prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1, keepalive); + } else { + prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL, filesize, keepalive); + } + return 0; + } + + if (readRangeHandler.isSingleRange()) { + // Possibly with zero sized file but should have been included + // in the FullFile case above + if (uranges.size() != 1) + return -1; + + // Only one range to return to the user + char buf[64]; + const off_t cnt = uranges[0].end - uranges[0].start + 1; + + XrdOucString s = "Content-Range: bytes "; + sprintf(buf, "%lld-%lld/%lld", (long long int)uranges[0].start, (long long int)uranges[0].end, filesize); + s += buf; + if (!responseHeader.empty()) { + s += "\r\n"; + s += responseHeader.c_str(); + } + + if (m_transfer_encoding_chunked && m_trailer_headers) { + prot->StartChunkedResp(206, NULL, (char *)s.c_str(), -1, keepalive); + } else { + prot->SendSimpleResp(206, NULL, (char *)s.c_str(), NULL, cnt, keepalive); + } + return 0; + } + + // Multiple reads to perform, compose and send the header + off_t cnt = 0; + for (auto &ur : uranges) { + cnt += ur.end - ur.start + 1; + + cnt += buildPartialHdr(ur.start, + ur.end, + filesize, + (char *) "123456").size(); + + } + cnt += buildPartialHdrEnd((char *) "123456").size(); + std::string header = "Content-Type: multipart/byteranges; boundary=123456"; + if (!m_digest_header.empty()) { + header += "\n"; + header += m_digest_header; + } + + if (m_transfer_encoding_chunked && m_trailer_headers) { + prot->StartChunkedResp(206, NULL, header.c_str(), -1, keepalive); + } else { + prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt, keepalive); + } + return 0; +} // This is invoked by the callbacks, after something has happened in the bridge @@ -1864,488 +2163,144 @@ int XrdHttpReq::PostProcessHTTPReq(bool final_) { } case XrdHttpReq::rtGET: { + // To duplicate the state diagram from the rtGET request state + // - 0: Perform an open request + // - 1: Perform a checksum request on the resource (only if requested in header; otherwise skipped) + // - 2: Perform a close (for directory listings only) + // - 3: Perform a dirlist + // - 4+: Reads from file; if at end, perform a close. + switch (reqstate) { + case 0: // open + { + if (xrdresp == kXR_ok) { + fopened = true; + getfhandle(); - if (xrdreq.header.requestid == ntohs(kXR_dirlist)) { - - - if (xrdresp == kXR_error) { - prot->SendSimpleResp(httpStatusCode, NULL, NULL, - httpStatusText.c_str(), httpStatusText.length(), false); - return -1; - } - - - if (stringresp.empty()) { - - // Start building the HTML response - stringresp = "\n" - "\n" - "\n" - "\n" - "\n" - "\n"; - - stringresp += ""; - stringresp += resource.c_str(); - stringresp += "\n"; - - stringresp += "\n" - "\n"; - - char *estr = escapeXML(resource.c_str()); - - stringresp += "

Listing of: "; - stringresp += estr; - stringresp += "

\n"; - - free(estr); - - stringresp += "
"; - - - stringresp += "\n" - "\n" - "" - "" - "" - "" - "" - "\n"; - - } - - // Now parse the answer building the entries vector - if (iovN > 0) { - char *startp = (char *) iovP[0].iov_base, *endp = 0; - char entry[1024]; - DirListInfo e; - while ( (size_t)(startp - (char *) iovP[0].iov_base) < (size_t)( iovP[0].iov_len - 1) ) { - // Find the filename, it comes before the \n - if ((endp = (char *) strchr((const char*) startp, '\n'))) { - strncpy(entry, (char *) startp, endp - startp); - entry[endp - startp] = 0; - e.path = entry; - - endp++; - - // Now parse the stat info - TRACEI(REQ, "Dirlist " << resource.c_str() << " entry=" << entry - << " stat=" << endp); + // Always try to parse response. In the case of a caching proxy, the open + // will have created the file in cache + if (iovP[1].iov_len > 1) { + TRACEI(REQ, "Stat for GET " << resource.c_str() + << " stat=" << (char *) iovP[1].iov_base); long dummyl; - sscanf(endp, "%ld %lld %ld %ld", - &dummyl, - &e.size, - &e.flags, - &e.modtime); - } else - strcpy(entry, (char *) startp); - - - if (e.path.length() && (e.path != ".") && (e.path != "..")) { - // The entry is filled. - std::string p = "" - ""; - p += "" - "" - "" - ""; - - stringresp += p; - - - } - - if (endp) { - char *pp = (char *)strchr((const char *)endp, '\n'); - if (pp) startp = pp+1; - else break; - } else break; + readRangeHandler.SetFilesize(filesize); - } - } - - // If this was the last bunch of entries, send the buffer and empty it immediately - if (final_) { - stringresp += "
ModeFlagsSizeModifiedName
file1.txt
"; - - if (e.flags & kXR_isDir) p += "d"; - else p += "-"; - - if (e.flags & kXR_other) p += "o"; - else p += "-"; - - if (e.flags & kXR_offline) p += "O"; - else p += "-"; - - if (e.flags & kXR_readable) p += "r"; - else p += "-"; - - if (e.flags & kXR_writable) p += "w"; - else p += "-"; - - if (e.flags & kXR_xset) p += "x"; - else p += "-"; - - p += "" + itos(e.flags) + "" + itos(e.size) + "" + ISOdatetime(e.modtime) + "" - ""; - p += e.path; - - free(estr); - - p += "



" - "

Request by "; - - if (prot->SecEntity.name) - stringresp += prot->SecEntity.name; - else - stringresp += prot->Link->ID; - - if (prot->SecEntity.vorg || - prot->SecEntity.name || - prot->SecEntity.moninfo || - prot->SecEntity.role) - stringresp += " ("; - - if (prot->SecEntity.vorg) { - stringresp += " VO: "; - stringresp += prot->SecEntity.vorg; - } - - if (prot->SecEntity.moninfo) { - stringresp += " DN: "; - stringresp += prot->SecEntity.moninfo; - } else - if (prot->SecEntity.name) { - stringresp += " DN: "; - stringresp += prot->SecEntity.name; - } - - - if (prot->SecEntity.role) { - stringresp += " Role: "; - stringresp += prot->SecEntity.role; - if (prot->SecEntity.endorsements) { - stringresp += " ("; - stringresp += prot->SecEntity.endorsements; - stringresp += ") "; - } - } - - - - if (prot->SecEntity.vorg || - prot->SecEntity.moninfo || - prot->SecEntity.role) - stringresp += " )"; - - if (prot->SecEntity.host) { - stringresp += " ( "; - stringresp += prot->SecEntity.host; - stringresp += " )"; - } - - stringresp += "

\n"; - stringresp += "

Powered by XrdHTTP "; - stringresp += XrdVSTRING; - stringresp += " (CERN IT-SDC)

\n"; - - prot->SendSimpleResp(200, NULL, NULL, (char *) stringresp.c_str(), 0, keepalive); - stringresp.clear(); - return keepalive ? 1 : -1; - } - - - } // end handling of dirlist - else - { // begin handling of open-read-close - - // To duplicate the state diagram from the rtGET request state - // - 0: Perform a stat on the resource - // - 1: Perform a checksum request on the resource (only if requested in header; otherwise skipped) - // - 2: Perform an open request (dirlist as appropriate). - // - 3+: Reads from file; if at end, perform a close. - switch (reqstate) { - case 0: //stat - { - // Ugly hack. Be careful with EOS! Test with vanilla XrdHTTP and EOS, separately - // A 404 on the preliminary stat() is fatal only - // in a manager. A non-manager will ignore the result and try anyway to open the file - // - if (xrdresp == kXR_ok) { - - if (iovN > 0) { - - // Now parse the stat info - TRACEI(REQ, "Stat for GET " << resource.c_str() - << " stat=" << (char *) iovP[0].iov_base); - - long dummyl; - sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld", - &dummyl, - &filesize, - &fileflags, - &filemodtime); - - readRangeHandler.SetFilesize(filesize); - - // We will default the response size specified by the headers; if that - // wasn't given, use the file size. - if (!length) { - length = filesize; - } - } - else { - TRACEI(REQ, "Can't find the stat information for '" - << resource.c_str() << "' Internal error?"); + // As above: if the client specified a response size, we use that. + // Otherwise, utilize the filesize + if (!length) { + length = filesize; } } - - // We are here if the request failed - - if (prot->myRole == kXR_isManager) { - prot->SendSimpleResp(httpStatusCode, NULL, NULL, - httpStatusText.c_str(), httpStatusText.length(), false); + else { + TRACEI(ALL, "GET returned no STAT information. Internal error?"); + prot->SendSimpleResp(500, NULL, NULL, "Storage system did not return stat info.", 0, false); return -1; } - - // We are here in the case of a negative response in a non-manager - return 0; - } // end stat - case 1: // checksum was requested and now we have its response. - { - return PostProcessChecksum(m_digest_header); - } - case 2: // open - { - if (xrdresp == kXR_ok) { - - getfhandle(); - - // Always try to parse response. In the case of a caching proxy, the open - // will have created the file in cache - if (iovP[1].iov_len > 1) { - TRACEI(REQ, "Stat for GET " << resource.c_str() - << " stat=" << (char *) iovP[1].iov_base); - - long dummyl; - sscanf((const char *) iovP[1].iov_base, "%ld %lld %ld %ld", - &dummyl, - &filesize, - &fileflags, - &filemodtime); - - readRangeHandler.SetFilesize(filesize); - - // As above: if the client specified a response size, we use that. - // Otherwise, utilize the filesize - if (!length) { - length = filesize; - } - } - else { - TRACEI(ALL, "GET returned no STAT information. Internal error?"); - } - - std::string responseHeader; - if (!m_digest_header.empty()) { - responseHeader = m_digest_header; - } - long one; - if (filemodtime && XrdOucEnv::Import("XRDPFC", one)) { - if (!responseHeader.empty()) { - responseHeader += "\r\n"; - } - long object_age = time(NULL) - filemodtime; - responseHeader += std::string("Age: ") + std::to_string(object_age < 0 ? 0 : object_age); - } - - const XrdHttpReadRangeHandler::UserRangeList &uranges = readRangeHandler.ListResolvedRanges(); - if (uranges.empty() && readRangeHandler.getError()) { - prot->SendSimpleResp(readRangeHandler.getError().httpRetCode, NULL, NULL, readRangeHandler.getError().errMsg.c_str(),0,false); - return -1; - } - - if (readRangeHandler.isFullFile()) { - // Full file. - - if (m_transfer_encoding_chunked && m_trailer_headers) { - prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1, keepalive); - } else { - prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL, filesize, keepalive); - } - return 0; - } - - if (readRangeHandler.isSingleRange()) { - // Possibly with zero sized file but should have been included - // in the FullFile case above - if (uranges.size() != 1) - return -1; - - // Only one range to return to the user - char buf[64]; - const off_t cnt = uranges[0].end - uranges[0].start + 1; - - XrdOucString s = "Content-Range: bytes "; - sprintf(buf, "%lld-%lld/%lld", (long long int)uranges[0].start, (long long int)uranges[0].end, filesize); - s += buf; - if (!responseHeader.empty()) { - s += "\r\n"; - s += responseHeader.c_str(); - } - - if (m_transfer_encoding_chunked && m_trailer_headers) { - prot->StartChunkedResp(206, NULL, (char *)s.c_str(), -1, keepalive); - } else { - prot->SendSimpleResp(206, NULL, (char *)s.c_str(), NULL, cnt, keepalive); - } - return 0; - } - - // Multiple reads to perform, compose and send the header - off_t cnt = 0; - for (auto &ur : uranges) { - cnt += ur.end - ur.start + 1; - - cnt += buildPartialHdr(ur.start, - ur.end, - filesize, - (char *) "123456").size(); - - } - cnt += buildPartialHdrEnd((char *) "123456").size(); - std::string header = "Content-Type: multipart/byteranges; boundary=123456"; - if (!m_digest_header.empty()) { - header += "\n"; - header += m_digest_header; - } - - if (m_transfer_encoding_chunked && m_trailer_headers) { - prot->StartChunkedResp(206, NULL, header.c_str(), -1, keepalive); - } else { - prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt, keepalive); - } - return 0; - - - } else { // xrdresp indicates an error occurred - - prot->SendSimpleResp(httpStatusCode, NULL, NULL, - httpStatusText.c_str(), httpStatusText.length(), false); - return -1; - } + } else if (xrderrcode == kXR_isDirectory) { // This is a directory; trigger directory-handling topic. + fileflags = kXR_isDir; + return 0; + } else { // xrdresp indicates an error occurred - // Case should not be reachable + prot->SendSimpleResp(httpStatusCode, NULL, NULL, + httpStatusText.c_str(), httpStatusText.length(), false); + return -1; + } + // Case should not be reachable + return -1; + } // end open + case 1: // checksum was requested and now we have its response. + { + return PostProcessChecksum(m_digest_header); + } + case 2: // close file handle in case of the directory + { + if (xrdresp != kXR_ok) { + prot->SendSimpleResp(httpStatusCode, NULL, NULL, + httpStatusText.c_str(), httpStatusText.length(), false); return -1; } - default: //read or readv + return 0; + } + case 3: // handle the directory listing response + { + return PostProcessListing(final_); + } + default: //read or readv, followed by a close. + { + // If we are postprocessing a close, potentially send out informational trailers + if ((ntohs(xrdreq.header.requestid) == kXR_close) || readClosing) { - // If we are postprocessing a close, potentially send out informational trailers - if ((ntohs(xrdreq.header.requestid) == kXR_close) || readClosing) - { - const XrdHttpReadRangeHandler::Error &rrerror = readRangeHandler.getError(); - if (rrerror) { - httpStatusCode = rrerror.httpRetCode; - httpStatusText = rrerror.errMsg; - } - - if (m_transfer_encoding_chunked && m_trailer_headers) { - if (prot->ChunkRespHeader(0)) - return -1; - - const std::string crlf = "\r\n"; - std::stringstream ss; - ss << "X-Transfer-Status: " << httpStatusCode << ": " << httpStatusText << crlf; - - const auto header = ss.str(); - if (prot->SendData(header.c_str(), header.size())) - return -1; - - if (prot->ChunkRespFooter()) - return -1; - } - - if (rrerror) return -1; - return keepalive ? 1 : -1; + const XrdHttpReadRangeHandler::Error &rrerror = readRangeHandler.getError(); + if (rrerror) { + httpStatusCode = rrerror.httpRetCode; + httpStatusText = rrerror.errMsg; } + + if (m_transfer_encoding_chunked && m_trailer_headers) { + if (prot->ChunkRespHeader(0)) + return -1; - // On error, we can only send out a message if trailers are enabled and the - // status response in trailer behavior is requested. - if (xrdresp == kXR_error) { - if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) { - // A trailer header is appropriate in this case; this is signified by - // a chunk with size zero, then the trailer, then a crlf. - // - // We only send the status trailer when explicitly requested; otherwise a - // "normal" HTTP client might simply see a short response and think it's a - // success - if (prot->ChunkRespHeader(0)) - return -1; - - const std::string crlf = "\r\n"; - std::stringstream ss; - ss << "X-Transfer-Status: " << httpStatusCode << ": " << httpStatusText << crlf; - - const auto header = ss.str(); - if (prot->SendData(header.c_str(), header.size())) - return -1; - - if (prot->ChunkRespFooter()) - return -1; + const std::string crlf = "\r\n"; + std::stringstream ss; + ss << "X-Transfer-Status: " << httpStatusCode << ": " << httpStatusText << crlf; + const auto header = ss.str(); + if (prot->SendData(header.c_str(), header.size())) return -1; - } else { + + if (prot->ChunkRespFooter()) return -1; - } } + if (rrerror) return -1; + return keepalive ? 1 : -1; + } - TRACEI(REQ, "Got data vectors to send:" << iovN); - - XrdHttpIOList received; - getReadResponse(received); + // On error, we can only send out a message if trailers are enabled and the + // status response in trailer behavior is requested. + if (xrdresp == kXR_error) { + sendFooterError(""); + return -1; + } - int rc; - if (readRangeHandler.isSingleRange()) { - rc = sendReadResponseSingleRange(received); - } else { - rc = sendReadResponsesMultiRanges(received); - } - if (rc) { - // make sure readRangeHandler will trigger close - // of file after next NextReadList(). - readRangeHandler.NotifyError(); - } - return 0; - } // end read or readv + TRACEI(REQ, "Got data vectors to send:" << iovN); - } // switch reqstate + XrdHttpIOList received; + getReadResponse(received); - } // End handling of the open-read-close case + int rc; + if (readRangeHandler.isSingleRange()) { + rc = sendReadResponseSingleRange(received); + } else { + rc = sendReadResponsesMultiRanges(received); + } + if (rc) { + // make sure readRangeHandler will trigger close + // of file after next NextReadList(). + readRangeHandler.NotifyError(); + } + return 0; + } // end read or readv + } // switch reqstate break; } // case GET - case XrdHttpReq::rtPUT: { if (!fopened) { @@ -2747,6 +2702,36 @@ int XrdHttpReq::PostProcessHTTPReq(bool final_) { return 0; } +void +XrdHttpReq::sendFooterError(const std::string &extra_text) { + if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) { + // A trailer header is appropriate in this case; this is signified by + // a chunk with size zero, then the trailer, then a crlf. + // + // We only send the status trailer when explicitly requested; otherwise a + // "normal" HTTP client might simply see a short response and think it's a + // success + + if (prot->ChunkRespHeader(0)) + return; + + std::stringstream ss; + ss << httpStatusCode << ": " << httpStatusText; + if (!extra_text.empty()) + ss << ": " << extra_text; + TRACEI(REQ, ss.str()); + ss << "\r\n"; + + const auto header = "X-Transfer-Status: " + ss.str(); + if (prot->SendData(header.c_str(), header.size())) + return; + + prot->ChunkRespFooter(); + } else { + TRACEI(REQ, httpStatusCode << ": " << httpStatusText << (extra_text.empty() ? "" : (": " + extra_text))); + } +} + void XrdHttpReq::reset() { TRACE(REQ, " XrdHttpReq request ended."); diff --git a/src/XrdHttp/XrdHttpReq.hh b/src/XrdHttp/XrdHttpReq.hh index ed56ede2066..1eee5cb45e3 100644 --- a/src/XrdHttp/XrdHttpReq.hh +++ b/src/XrdHttp/XrdHttpReq.hh @@ -107,6 +107,14 @@ private: // be included in the response. int PostProcessChecksum(std::string &digest_header); + // Process the listing request of a GET request against a directory + // - final_: True if this is the last entry in the listing. + int PostProcessListing(bool final_); + + // Send the response for a GET request for a file read (i.e., not a directory) + // Invoked after the open is successful but before the first read is issued. + int ReturnGetHeaders(); + /// Cook and send the response after the bridge did something /// Return values: /// 0->everything OK, additionsl steps may be required @@ -135,6 +143,10 @@ private: // the data and necessary headers, assuming multipart/byteranges content type. int sendReadResponsesMultiRanges(const XrdHttpIOList &received); + // If requested by the client, sends any I/O errors that occur during the transfer + // into a footer. + void sendFooterError(const std::string &); + /** * Extract a comma separated list of checksums+metadata into a vector * @param checksumList the list like "0:sha1, 1:adler32, 2:md5" From 0588dcac18c27d3565e7108f1933d88c4720ebc7 Mon Sep 17 00:00:00 2001 From: Brian Bockelman Date: Thu, 24 Oct 2024 10:55:33 -0500 Subject: [PATCH 231/276] Reset the state after processing while in redrive Each time we process successfully from the redrive thread, we should decrement the status counter. Otherwise, after multiple requests, the attempt to `Run` from XrdHttp will fail. This was observed to cause failures in closing files 100% of the time when the open was delayed, causing failures in the XrdHttp response. This additionally adds logging on failures for submitting the run. --- src/XrdXrootd/XrdXrootdTransit.cc | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/XrdXrootd/XrdXrootdTransit.cc b/src/XrdXrootd/XrdXrootdTransit.cc index 52d454de7c6..57720b78ac0 100644 --- a/src/XrdXrootd/XrdXrootdTransit.cc +++ b/src/XrdXrootd/XrdXrootdTransit.cc @@ -463,6 +463,11 @@ void XrdXrootdTransit::Redrive() if (rc == 0) { rc = realProt->Process(NULL); } + if (runStatus) + {AtomicBeg(runMutex); + AtomicZAP(runStatus); + AtomicEnd(runMutex); + } } while((rc == 0) && !runError && !runWait); } else rc = Send(kXR_error, ioV, 2, 0); @@ -567,7 +572,10 @@ bool XrdXrootdTransit::Run(const char *xreqP, char *xdataP, int xdataL) AtomicBeg(runMutex); rc = AtomicInc(runStatus); AtomicEnd(runMutex); - if (rc) return false; + if (rc) + {TRACEP(REQ, "Bridge request failed due to re-entry"); + return false; + } // Copy the request header // @@ -579,13 +587,17 @@ bool XrdXrootdTransit::Run(const char *xreqP, char *xdataP, int xdataL) if (Request.header.requestid & 0x8000 || Request.header.requestid > static_cast(kXR_truncate) || !reqTab[Request.header.requestid - kXR_auth]) - return Fail(kXR_Unsupported, "Unsupported bridge request"); + {TRACEP(REQ, "Unsupported bridge request"); + return Fail(kXR_Unsupported, "Unsupported bridge request"); + } // Validate the data length // Request.header.dlen = ntohl(Request.header.dlen); if (Request.header.dlen < 0) - return Fail(kXR_ArgInvalid, "Invalid request data length"); + {TRACEP(REQ, "Invalid request data length"); + return Fail(kXR_ArgInvalid, "Invalid request data length"); + } // Copy the stream id and trace this request // @@ -607,7 +619,9 @@ bool XrdXrootdTransit::Run(const char *xreqP, char *xdataP, int xdataL) if (!runArgs || movLen > runABsz) {if (runArgs) free(runArgs); if (!(runArgs = (char *)malloc(movLen))) - return Fail(kXR_NoMemory, "Insufficient memory"); + {TRACEP(REQ, "Failed to allocate memory"); + return Fail(kXR_NoMemory, "Insufficient memory"); + } runABsz = movLen; } memcpy(runArgs, xdataP, movLen); runALen = movLen; From d74b2af36d04c398a91235bd2de32ee1283e92fe Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Thu, 21 Nov 2024 14:06:25 +0100 Subject: [PATCH 232/276] [XrdCl] Downgrade force disconnect error message to debug level Fixes: #2370, #2288 --- src/XrdCl/XrdClStream.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/XrdCl/XrdClStream.cc b/src/XrdCl/XrdClStream.cc index da99819ee62..1b602dbb5d5 100644 --- a/src/XrdCl/XrdClStream.cc +++ b/src/XrdCl/XrdClStream.cc @@ -921,7 +921,7 @@ namespace XrdCl pSubStreams[substream]->status = Socket::Disconnected; if( !hush ) - log->Error( PostMasterMsg, "[%s] Forcing error on disconnect: %s.", + log->Debug( PostMasterMsg, "[%s] Forcing error on disconnect: %s.", pStreamName.c_str(), status.ToString().c_str() ); //-------------------------------------------------------------------- From c413daa9260a6bf7d877a6b8a86430c4af4fd7cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matev=C5=BE=20Tadel?= Date: Fri, 22 Nov 2024 01:40:09 -0800 Subject: [PATCH 233/276] [Pfc] Reduce verbosity of errors originating from remote block reads Report each error only once for each IO object, then report total counts for each error type once the IO object is detached. Previous repeating errors have been downgraded to Debug level. --- src/XrdPfc/XrdPfcConfiguration.cc | 5 +++++ src/XrdPfc/XrdPfcFile.cc | 17 ++++++++++++----- src/XrdPfc/XrdPfcFile.hh | 5 +++-- src/XrdPfc/XrdPfcIO.hh | 10 ++++++++++ src/XrdPfc/XrdPfcIOFile.cc | 27 ++++++++++++++++++++++----- src/XrdPfc/XrdPfcIOFile.hh | 2 -- src/XrdPfc/XrdPfcTrace.hh | 13 +++++++++++-- 7 files changed, 63 insertions(+), 16 deletions(-) diff --git a/src/XrdPfc/XrdPfcConfiguration.cc b/src/XrdPfc/XrdPfcConfiguration.cc index 2cdb5869394..075b3dbf7d9 100644 --- a/src/XrdPfc/XrdPfcConfiguration.cc +++ b/src/XrdPfc/XrdPfcConfiguration.cc @@ -15,6 +15,11 @@ #include +namespace XrdPfc +{ + const char *trace_what_strings[] = {"","error ","warning ","info ","debug ","dump "}; +} + using namespace XrdPfc; XrdVERSIONINFO(XrdOucGetCache, XrdPfc); diff --git a/src/XrdPfc/XrdPfcFile.cc b/src/XrdPfc/XrdPfcFile.cc index 178d1205c5b..1c4bea4c9e5 100644 --- a/src/XrdPfc/XrdPfcFile.cc +++ b/src/XrdPfc/XrdPfcFile.cc @@ -923,7 +923,8 @@ int File::ReadOpusCoalescere(IO *io, const XrdOucIOVec *readV, int readVnum, if (read_req) { read_req->m_bytes_read += bytes_read; - read_req->update_error_cond(error_cond); + if (error_cond) + read_req->update_error_cond(error_cond); read_req->m_stats.m_BytesHit += bytes_read; read_req->m_sync_done = true; @@ -1217,7 +1218,7 @@ void File::ProcessBlockError(Block *b, ReadRequest *rreq) // Does not manage m_read_req. // Will not complete the request. - TRACEF(Error, "ProcessBlockError() io " << b->m_io << ", block "<< b->m_offset/m_block_size << + TRACEF(Debug, "ProcessBlockError() io " << b->m_io << ", block "<< b->m_offset/m_block_size << " finished with error " << -b->get_error() << " " << XrdSysE2T(-b->get_error())); rreq->update_error_cond(b->get_error()); @@ -1353,9 +1354,15 @@ void File::ProcessBlockResponse(Block *b, int res) else { if (res < 0) { - TRACEF(Error, tpfx << "block " << b << ", idx=" << b->m_offset/m_block_size << ", off=" << b->m_offset << " error=" << res); + bool new_error = b->get_io()->register_block_error(res); + int tlvl = new_error ? TRACE_Error : TRACE_Debug; + TRACEF_INT(tlvl, tpfx << "block " << b << ", idx=" << b->m_offset/m_block_size << ", off=" << b->m_offset + << ", io=" << b->get_io() << ", error=" << res); } else { - TRACEF(Error, tpfx << "block " << b << ", idx=" << b->m_offset/m_block_size << ", off=" << b->m_offset << " incomplete, got " << res << " expected " << b->get_size()); + bool first_p = b->get_io()->register_incomplete_read(); + int tlvl = first_p ? TRACE_Error : TRACE_Debug; + TRACEF_INT(tlvl, tpfx << "block " << b << ", idx=" << b->m_offset/m_block_size << ", off=" << b->m_offset + << ", io=" << b->get_io() << " incomplete, got " << res << " expected " << b->get_size()); #if defined(__APPLE__) || defined(__GNU__) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) || defined(__FreeBSD__) res = -EIO; #else @@ -1393,7 +1400,7 @@ void File::ProcessBlockResponse(Block *b, int res) { ReadRequest *rreq = creqs_to_keep.front().m_read_req; - TRACEF(Info, "ProcessBlockResponse() requested block " << (void*)b << " failed with another io " << + TRACEF(Debug, "ProcessBlockResponse() requested block " << (void*)b << " failed with another io " << b->get_io() << " - reissuing request with my io " << rreq->m_io); b->reset_error_and_set_io(rreq->m_io, rreq); diff --git a/src/XrdPfc/XrdPfcFile.hh b/src/XrdPfc/XrdPfcFile.hh index 608e5dd73e3..8a71894a379 100644 --- a/src/XrdPfc/XrdPfcFile.hh +++ b/src/XrdPfc/XrdPfcFile.hh @@ -78,6 +78,7 @@ struct ReadRequest long long m_bytes_read = 0; int m_error_cond = 0; // to be set to -errno + int m_error_count = 0; Stats m_stats; int m_n_chunk_reqs = 0; @@ -88,7 +89,7 @@ struct ReadRequest m_io(io), m_rh(rh) {} - void update_error_cond(int ec) { if (m_error_cond == 0 ) m_error_cond = ec; } + void update_error_cond(int ec) { ++m_error_count; if (m_error_cond == 0 ) m_error_cond = ec; } bool is_complete() const { return m_n_chunk_reqs == 0 && m_sync_done && m_direct_done; } int return_value() const { return m_error_cond ? m_error_cond : m_bytes_read; } @@ -311,7 +312,7 @@ private: static const char *m_traceID; int m_ref_cnt; //!< number of references from IO or sync - + XrdOssDF *m_data_file; //!< file handle for data file on disk XrdOssDF *m_info_file; //!< file handle for data-info file on disk Info m_cfi; //!< download status of file blocks and access statistics diff --git a/src/XrdPfc/XrdPfcIO.hh b/src/XrdPfc/XrdPfcIO.hh index 1e5f5885b71..a8298927c2a 100644 --- a/src/XrdPfc/XrdPfcIO.hh +++ b/src/XrdPfc/XrdPfcIO.hh @@ -85,6 +85,16 @@ private: int m_active_prefetches {0}; bool m_allow_prefetching {true}; bool m_in_detach {false}; + +protected: + int m_incomplete_count {0}; + std::map m_error_counts; + bool register_incomplete_read() { + return m_incomplete_count++ == 0; + } + bool register_block_error(int res) { + return m_error_counts[res]++ == 0; + } }; } diff --git a/src/XrdPfc/XrdPfcIOFile.cc b/src/XrdPfc/XrdPfcIOFile.cc index d0b3ebef169..1a54db38021 100644 --- a/src/XrdPfc/XrdPfcIOFile.cc +++ b/src/XrdPfc/XrdPfcIOFile.cc @@ -153,11 +153,28 @@ void IOFile::DetachFinalize() { // Effectively a destructor. - TRACE(Info, "DetachFinalize() " << this); + TRACE(Debug, "DetachFinalize() " << this); m_file->RequestSyncOfDetachStats(); Cache::GetInstance().ReleaseFile(m_file, this); + if (( ! m_error_counts.empty() || m_incomplete_count > 0) && XRD_TRACE What >= TRACE_Error) { + char info[1024]; + int pos = 0, cap = 1024; + bool truncated = false; + for (auto [err, cnt] : m_error_counts) { + int len = snprintf(&info[pos], cap, " ( %d : %d)", err, cnt); + if (len >= cap) { + truncated = true; + break; + } + pos += len; + cap -= len; + } + TRACE(Error, "DetachFinalize() " << this << " n_incomplete_reads=" << m_incomplete_count + << ", block (error : count) report:" << info << (truncated ? " - truncated" : "")); + } + delete this; } @@ -273,9 +290,9 @@ int IOFile::ReadEnd(int retval, ReadReqRH *rh) TRACEIO(Dump, "ReadEnd() " << (rh->m_iocb ? "a" : "") << "sync " << this << " sid: " << Xrd::hex1 << rh->m_seq_id << " retval: " << retval << " expected_size: " << rh->m_expected_size); if (retval < 0) { - TRACEIO(Warning, "ReadEnd() error in File::Read(), exit status=" << retval << ", error=" << XrdSysE2T(-retval) << " sid: " << Xrd::hex1 << rh->m_seq_id); + TRACEIO(Debug, "ReadEnd() error in File::Read(), exit status=" << retval << ", error=" << XrdSysE2T(-retval) << " sid: " << Xrd::hex1 << rh->m_seq_id); } else if (retval < rh->m_expected_size) { - TRACEIO(Warning, "ReadEnd() bytes missed " << rh->m_expected_size - retval << " sid: " << Xrd::hex1 << rh->m_seq_id); + TRACEIO(Debug, "ReadEnd() bytes missed " << rh->m_expected_size - retval << " sid: " << Xrd::hex1 << rh->m_seq_id); } if (rh->m_iocb) rh->m_iocb->Done(retval); @@ -362,9 +379,9 @@ int IOFile::ReadVEnd(int retval, ReadReqRH *rh) " retval: " << retval << " n_chunks: " << rh->m_n_chunks << " expected_size: " << rh->m_expected_size); if (retval < 0) { - TRACEIO(Warning, "ReadVEnd() error in File::ReadV(), exit status=" << retval << ", error=" << XrdSysE2T(-retval)); + TRACEIO(Debug, "ReadVEnd() error in File::ReadV(), exit status=" << retval << ", error=" << XrdSysE2T(-retval)); } else if (retval < rh->m_expected_size) { - TRACEIO(Warning, "ReadVEnd() bytes missed " << rh->m_expected_size - retval); + TRACEIO(Debug, "ReadVEnd() bytes missed " << rh->m_expected_size - retval); } if (rh->m_iocb) rh->m_iocb->Done(retval); diff --git a/src/XrdPfc/XrdPfcIOFile.hh b/src/XrdPfc/XrdPfcIOFile.hh index 7e771dbbfb2..b63941b8974 100644 --- a/src/XrdPfc/XrdPfcIOFile.hh +++ b/src/XrdPfc/XrdPfcIOFile.hh @@ -88,8 +88,6 @@ private: struct stat *m_localStat; int initCachedStat(); - - }; } diff --git a/src/XrdPfc/XrdPfcTrace.hh b/src/XrdPfc/XrdPfcTrace.hh index 4f69bfeca7a..38f2719bd7a 100644 --- a/src/XrdPfc/XrdPfcTrace.hh +++ b/src/XrdPfc/XrdPfcTrace.hh @@ -38,6 +38,11 @@ #define XRD_TRACE GetTrace()-> #endif +namespace XrdPfc +{ + extern const char *trace_what_strings[]; +} + #define ERRNO_AND_ERRSTR(err_code) ", err_code=" << err_code << ", err_str=" << XrdSysE2T(err_code) #define TRACE(act, x) \ @@ -46,8 +51,7 @@ #define TRACE_INT(act, x) \ if (XRD_TRACE What >= act) \ - {static const char* t_what[]={"","error ","warning ","info ","debug ","dump "};\ - SYSTRACE(XRD_TRACE, 0, m_traceID, 0, t_what[act] << x)} + SYSTRACE(XRD_TRACE, 0, m_traceID, 0, trace_what_strings[act] << x) #define TRACE_TEST(act, x) \ SYSTRACE(XRD_TRACE, 0, m_traceID, 0, TRACE_STR_ ## act << x) @@ -64,6 +68,10 @@ if (XRD_TRACE What >= TRACE_ ## act) SYSTRACE(XRD_TRACE, 0, m_traceID, 0, \ TRACE_STR_ ## act << x << " " << GetLocalPath()) +#define TRACEF_INT(act, x) \ + if (XRD_TRACE What >= act) \ + SYSTRACE(XRD_TRACE, 0, m_traceID, 0, trace_what_strings[act] << x << " " << GetLocalPath()) + #else #define ERRNO_AND_ERRSTR(err_code) @@ -71,6 +79,7 @@ #define TRACE_PC(act, pre_code, x) #define TRACEIO(act, x) #define TRACEF(act, x) +#define TRACEF_INT(act, x) #endif From 578e079d9dbcbf16b83e2335866d10d4e1e640d2 Mon Sep 17 00:00:00 2001 From: Matevz Tadel Date: Wed, 20 Nov 2024 12:36:52 -0800 Subject: [PATCH 234/276] [Pfc] Add num bytes written to disk and bytes prefetched to the gstream record. --- src/XrdPfc/XrdPfc.cc | 6 ++++-- src/XrdPfc/XrdPfcFile.cc | 2 ++ src/XrdPfc/XrdPfcFile.hh | 2 ++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/XrdPfc/XrdPfc.cc b/src/XrdPfc/XrdPfc.cc index 9d411ced2c6..c074fbb2f68 100644 --- a/src/XrdPfc/XrdPfc.cc +++ b/src/XrdPfc/XrdPfc.cc @@ -648,12 +648,14 @@ void Cache::dec_ref_cnt(File* f, bool high_debug) int len = snprintf(buf, 4096, "{\"event\":\"file_close\"," "\"lfn\":\"%s\",\"size\":%lld,\"blk_size\":%d,\"n_blks\":%d,\"n_blks_done\":%d," "\"access_cnt\":%lu,\"attach_t\":%lld,\"detach_t\":%lld,\"remotes\":%s," - "\"b_hit\":%lld,\"b_miss\":%lld,\"b_bypass\":%lld,\"n_cks_errs\":%d}", + "\"b_hit\":%lld,\"b_miss\":%lld,\"b_bypass\":%lld," + "\"b_todisk\":%lld,\"b_prefetch\":%lld,\"n_cks_errs\":%d}", f->GetLocalPath().c_str(), f->GetFileSize(), f->GetBlockSize(), f->GetNBlocks(), f->GetNDownloadedBlocks(), (unsigned long) f->GetAccessCnt(), (long long) as->AttachTime, (long long) as->DetachTime, f->GetRemoteLocations().c_str(), - as->BytesHit, as->BytesMissed, as->BytesBypassed, st.m_NCksumErrors + as->BytesHit, as->BytesMissed, as->BytesBypassed, + st.m_BytesWritten, f->GetPrefetchedBytes(), st.m_NCksumErrors ); bool suc = false; if (len < 4096) diff --git a/src/XrdPfc/XrdPfcFile.cc b/src/XrdPfc/XrdPfcFile.cc index 1c4bea4c9e5..d2db3337693 100644 --- a/src/XrdPfc/XrdPfcFile.cc +++ b/src/XrdPfc/XrdPfcFile.cc @@ -68,6 +68,7 @@ File::File(const std::string& path, long long iOffset, long long iFileSize) : m_block_size(0), m_num_blocks(0), m_prefetch_state(kOff), + m_prefetch_bytes(0), m_prefetch_read_cnt(0), m_prefetch_hit_cnt(0), m_prefetch_score(0) @@ -1321,6 +1322,7 @@ void File::ProcessBlockResponse(Block *b, int res) m_state_cond.UnLock(); return; } + m_prefetch_bytes += b->get_size(); } else { diff --git a/src/XrdPfc/XrdPfcFile.hh b/src/XrdPfc/XrdPfcFile.hh index 8a71894a379..d1d0672cb1c 100644 --- a/src/XrdPfc/XrdPfcFile.hh +++ b/src/XrdPfc/XrdPfcFile.hh @@ -292,6 +292,7 @@ public: int GetBlockSize() const { return m_cfi.GetBufferSize(); } int GetNBlocks() const { return m_cfi.GetNBlocks(); } int GetNDownloadedBlocks() const { return m_cfi.GetNDownloadedBlocks(); } + long long GetPrefetchedBytes() const { return m_prefetch_bytes; } const Stats& RefStats() const { return m_stats; } // These three methods are called under Cache's m_active lock @@ -365,6 +366,7 @@ private: PrefetchState_e m_prefetch_state; + long long m_prefetch_bytes; int m_prefetch_read_cnt; int m_prefetch_hit_cnt; float m_prefetch_score; // cached From 78d1fb7b12a8a136606108de7a2399baf4e7e509 Mon Sep 17 00:00:00 2001 From: Matevz Tadel Date: Wed, 20 Nov 2024 10:49:41 -0800 Subject: [PATCH 235/276] [Pfc] Stat call: fix behavior and improve performance, part of #2349 - For most parts, uses stat of the data file for reporting. - st_size is always set to the full size of the remote file. This is either obtained from cinfo file or from xattr variable created when the file is initially placed into cache (this is a new optimization). - st_atim is set to 0 when the file is not considered to be fully cached, as determined by the pfc.onlyifcached config settings. --- src/XrdPfc/XrdPfc.cc | 276 +++++++++++++++++------------- src/XrdPfc/XrdPfc.hh | 7 + src/XrdPfc/XrdPfcConfiguration.cc | 80 ++++++++- src/XrdPfc/XrdPfcFile.cc | 22 +++ src/XrdPfc/XrdPfcFile.hh | 2 + src/XrdPfc/XrdPfcIOFile.cc | 82 +++------ src/XrdPfc/XrdPfcIOFile.hh | 3 +- 7 files changed, 288 insertions(+), 184 deletions(-) diff --git a/src/XrdPfc/XrdPfc.cc b/src/XrdPfc/XrdPfc.cc index c074fbb2f68..7c7a09c74b0 100644 --- a/src/XrdPfc/XrdPfc.cc +++ b/src/XrdPfc/XrdPfc.cc @@ -31,6 +31,7 @@ #include "XrdSys/XrdSysPthread.hh" #include "XrdSys/XrdSysTimer.hh" #include "XrdSys/XrdSysTrace.hh" +#include "XrdSys/XrdSysXAttr.hh" #include "XrdXrootd/XrdXrootdGStream.hh" @@ -43,6 +44,8 @@ #include "XrdPfcIOFile.hh" #include "XrdPfcIOFileBlock.hh" +extern XrdSysXAttr *XrdSysXAttrActive; + using namespace XrdPfc; Cache * Cache::m_instance = 0; @@ -411,7 +414,7 @@ void Cache::ReleaseRAM(char* buf, long long size) File* Cache::GetFile(const std::string& path, IO* io, long long off, long long filesize) { - // Called from virtual IO::Attach + // Called from virtual IOFile constructor. TRACE(Debug, "GetFile " << path << ", io " << io); @@ -447,6 +450,7 @@ File* Cache::GetFile(const std::string& path, IO* io, long long off, long long f } } + // This is always true, now that IOFileBlock is unsupported. if (filesize == 0) { struct stat st; @@ -504,7 +508,7 @@ void Cache::ReleaseFile(File* f, IO* io) dec_ref_cnt(f, true); } - + namespace { @@ -581,7 +585,8 @@ void Cache::inc_ref_cnt(File* f, bool lock, bool high_debug) void Cache::dec_ref_cnt(File* f, bool high_debug) { - // Called from ReleaseFile() or DiskSync callback. + // NOT under active lock. + // Called from ReleaseFile(), DiskSync callback and stat-like functions. int tlvl = high_debug ? TRACE_Debug : TRACE_Dump; int cnt; @@ -590,6 +595,7 @@ void Cache::dec_ref_cnt(File* f, bool high_debug) XrdSysCondVarHelper lock(&m_active_cond); cnt = f->get_ref_cnt(); + TRACE_INT(tlvl, "dec_ref_cnt " << f->GetLocalPath() << ", cnt at entry = " << cnt); if (f->is_in_emergency_shutdown()) { @@ -607,13 +613,15 @@ void Cache::dec_ref_cnt(File* f, bool high_debug) TRACE_INT(tlvl, "dec_ref_cnt " << f->GetLocalPath() << " is in shutdown, ref_cnt = " << cnt << " -- waiting"); } - + return; + } + if (cnt > 1) + { + f->dec_ref_cnt(); return; } } - TRACE_INT(tlvl, "dec_ref_cnt " << f->GetLocalPath() << ", cnt at entry = " << cnt); - if (cnt == 1) { if (f->FinalizeSyncBeforeExit()) @@ -899,6 +907,78 @@ int Cache::LocalFilePath(const char *curl, char *buff, int blen, return -ENOENT; } +//______________________________________________________________________________ +// If supported, write file_size as xattr to cinfo file. +//------------------------------------------------------------------------------ +void Cache::WriteFileSizeXAttr(int cinfo_fd, long long file_size) +{ + if (m_metaXattr) { + int res = XrdSysXAttrActive->Set("pfc.fsize", &file_size, sizeof(long long), 0, cinfo_fd, 0); + if (res != 0) { + TRACE(Debug, "WriteFileSizeXAttr error setting xattr " << res); + } + } +} + +//______________________________________________________________________________ +// Determine full size of the data file from the corresponding cinfo-file name. +// Attempts to read xattr first and falls back to reading of the cinfo file. +// Returns -error on failure. +//------------------------------------------------------------------------------ +long long Cache::DetermineFullFileSize(const std::string &cinfo_fname) +{ + if (m_metaXattr) { + char pfn[4096]; + m_oss->Lfn2Pfn(cinfo_fname.c_str(), pfn, 4096); + long long fsize = -1ll; + int res = XrdSysXAttrActive->Get("pfc.fsize", &fsize, sizeof(long long), pfn); + if (res == sizeof(long long)) + { + return fsize; + } + else + { + TRACE(Debug, "DetermineFullFileSize error getting xattr " << res); + } + } + + XrdOssDF *infoFile = m_oss->newFile(m_configuration.m_username.c_str()); + XrdOucEnv env; + int res = infoFile->Open(cinfo_fname.c_str(), O_RDONLY, 0600, env); + if (res < 0) + return res; + Info info(m_trace, 0); + if ( ! info.Read(infoFile, cinfo_fname.c_str())) + return -EBADF; + return info.GetFileSize(); +} + +//______________________________________________________________________________ +// Calculate if the file is to be considered cached for the purposes of +// only-if-cached and setting of atime of the Stat() calls. +// Returns true if the file is to be conidered cached. +//------------------------------------------------------------------------------ +bool Cache::DecideIfConsideredCached(long long file_size, long long bytes_on_disk) +{ + if (file_size == 0 || bytes_on_disk >= file_size) + return true; + + double frac_on_disk = (double) bytes_on_disk / file_size; + + if (file_size <= m_configuration.m_onlyIfCachedMinSize) + { + if (frac_on_disk >= m_configuration.m_onlyIfCachedMinFrac) + return true; + } + else + { + if (bytes_on_disk >= m_configuration.m_onlyIfCachedMinSize && + frac_on_disk >= m_configuration.m_onlyIfCachedMinFrac) + return true; + } + return false; +} + //______________________________________________________________________________ // Check if the file is cached including m_onlyIfCachedMinSize and m_onlyIfCachedMinFrac // pfc configuration parameters. The logic of accessing the Info file is the same @@ -907,108 +987,58 @@ int Cache::LocalFilePath(const char *curl, char *buff, int blen, //! the buffer, if it has been supllied. //! //! @return <0 - the request could not be fulfilled. The return value is -//! -errno describing why. If a buffer was supplied and a -//! path could be generated it is returned only if "why" is -//! ForCheck or ForInfo. Otherwise, a null path is returned. +//! -errno describing why. //! //! @return >0 - Reserved for future use. //------------------------------------------------------------------------------ int Cache::ConsiderCached(const char *curl) { - TRACE(Debug, "ConsiderFileCached '" << curl << "'" ); + static const char* tpfx = "ConsiderCached "; + + TRACE(Debug, tpfx << curl); XrdCl::URL url(curl); std::string f_name = url.GetPath(); - std::string i_name = f_name + Info::s_infoExtension; + File *file = nullptr; { XrdSysCondVarHelper lock(&m_active_cond); - m_purge_delay_set.insert(f_name); + auto it = m_active.find(f_name); + if (it != m_active.end()) { + file = it->second; + inc_ref_cnt(file, false, false); + } + } + if (file) { + struct stat sbuff; + int res = file->Fstat(sbuff); + dec_ref_cnt(file, false); + if (res) + return res; + // DecideIfConsideredCached() already called in File::Fstat(). + return sbuff.st_atime > 0 ? 0 : -EREMOTE; } - struct stat sbuff, sbuff2; - if (m_oss->Stat(f_name.c_str(), &sbuff) == XrdOssOK && - m_oss->Stat(i_name.c_str(), &sbuff2) == XrdOssOK) + struct stat sbuff; + int res = m_oss->Stat(f_name.c_str(), &sbuff); + if (res != XrdOssOK) { + TRACE(Debug, tpfx << curl << " -> " << res); + return res; + } + if (S_ISDIR(sbuff.st_mode)) { - if (S_ISDIR(sbuff.st_mode)) - { - TRACE(Info, "ConsiderCached '" << curl << ", why=ForInfo" << " -> EISDIR"); - return -EISDIR; - } - else - { - bool read_ok = false; - bool is_cached = false; - - // Lock and check if the file is active. If NOT, keep the lock - // and add dummy access after successful reading of info file. - // If it IS active, just release the lock, this ongoing access will - // assure the file continues to exist. - - // XXXX How can I just loop over the cinfo file when active? - // Can I not get is_complete from the existing file? - // Do I still want to inject access record? - // Oh, it writes only if not active .... still let's try to use existing File. - - m_active_cond.Lock(); - - bool is_active = m_active.find(f_name) != m_active.end(); - - if (is_active) - m_active_cond.UnLock(); - - XrdOssDF *infoFile = m_oss->newFile(m_configuration.m_username.c_str()); - XrdOucEnv myEnv; - int res = infoFile->Open(i_name.c_str(), O_RDWR, 0600, myEnv); - if (res >= 0) - { - Info info(m_trace, 0); - if (info.Read(infoFile, i_name.c_str())) - { - read_ok = true; - - if (info.IsComplete()) - { - is_cached = true; - } - else if (info.GetFileSize() == 0) - { - is_cached = true; - } - else - { - long long fileSize = info.GetFileSize(); - long long bytesRead = info.GetNDownloadedBytes(); - - if (fileSize < m_configuration.m_onlyIfCachedMinSize) - { - if ((float)bytesRead / fileSize > m_configuration.m_onlyIfCachedMinFrac) - is_cached = true; - } - else - { - if (bytesRead > m_configuration.m_onlyIfCachedMinSize && - (float)bytesRead / fileSize > m_configuration.m_onlyIfCachedMinFrac) - is_cached = true; - } - } - } - infoFile->Close(); - } - delete infoFile; - - if (!is_active) m_active_cond.UnLock(); + TRACE(Debug, tpfx << curl << " -> EISDIR"); + return -EISDIR; + } - if (read_ok) - { - TRACE(Info, "ConsiderCached '" << curl << "', why=ForInfo" << (is_cached ? " -> FILE_COMPLETE_IN_CACHE" : " -> EREMOTE")); - return is_cached ? 0 : -EREMOTE; - } - } + long long file_size = DetermineFullFileSize(f_name + Info::s_infoExtension); + if (file_size < 0) { + TRACE(Debug, tpfx << curl << " -> " << file_size); + return (int) file_size; } + bool is_cached = DecideIfConsideredCached(file_size, sbuff.st_blocks * 512ll); - TRACE(Info, "ConsiderCached '" << curl << "', why=ForInfo" << " -> ENOENT"); - return -ENOENT; + return is_cached ? 0 : -EREMOTE; } //______________________________________________________________________________ @@ -1053,8 +1083,7 @@ int Cache::Prepare(const char *curl, int oflags, mode_t mode) } struct stat sbuff; - int res = m_oss->Stat(i_name.c_str(), &sbuff); - if (res == 0) + if (m_oss->Stat(i_name.c_str(), &sbuff) == XrdOssOK) { TRACE(Dump, "Prepare defer open " << f_name); return 1; @@ -1075,44 +1104,51 @@ int Cache::Prepare(const char *curl, int oflags, mode_t mode) int Cache::Stat(const char *curl, struct stat &sbuff) { + const char *tpfx = "Stat "; + XrdCl::URL url(curl); std::string f_name = url.GetPath(); + File *file = nullptr; { XrdSysCondVarHelper lock(&m_active_cond); - m_purge_delay_set.insert(f_name); + auto it = m_active.find(f_name); + if (it != m_active.end()) { + file = it->second; + inc_ref_cnt(file, false, false); + } + } + if (file) { + int res = file->Fstat(sbuff); + dec_ref_cnt(file, false); + TRACE(Debug, tpfx << "from active file " << curl << " -> " << res); + return res; } - if (m_oss->Stat(f_name.c_str(), &sbuff) == XrdOssOK) + int res = m_oss->Stat(f_name.c_str(), &sbuff); + if (res != XrdOssOK) { + TRACE(Debug, tpfx << curl << " -> " << res); + return res; + } + if (S_ISDIR(sbuff.st_mode)) { - if (S_ISDIR(sbuff.st_mode)) - { - return 0; - } - else - { - bool success = false; - XrdOssDF* infoFile = m_oss->newFile(m_configuration.m_username.c_str()); - XrdOucEnv myEnv; + TRACE(Debug, tpfx << curl << " -> EISDIR"); + return -EISDIR; + } - f_name += Info::s_infoExtension; - int res = infoFile->Open(f_name.c_str(), O_RDONLY, 0600, myEnv); - if (res >= 0) - { - Info info(m_trace, 0); - if (info.Read(infoFile, f_name.c_str())) - { - sbuff.st_size = info.GetFileSize(); - success = true; - } - } - infoFile->Close(); - delete infoFile; - return success ? 0 : 1; - } + long long file_size = DetermineFullFileSize(f_name + Info::s_infoExtension); + if (file_size < 0) { + TRACE(Debug, tpfx << curl << " -> " << file_size); + return (int) file_size; } + sbuff.st_size = file_size; + bool is_cached = DecideIfConsideredCached(file_size, sbuff.st_blocks * 512ll); + if ( ! is_cached) + sbuff.st_atime = 0; + + TRACE(Debug, tpfx << "from disk " << curl << " -> " << res); - return 1; + return 0; } //______________________________________________________________________________ diff --git a/src/XrdPfc/XrdPfc.hh b/src/XrdPfc/XrdPfc.hh index fdc4093dd70..f22591eb115 100644 --- a/src/XrdPfc/XrdPfc.hh +++ b/src/XrdPfc/XrdPfc.hh @@ -300,6 +300,10 @@ public: //--------------------------------------------------------------------- virtual int ConsiderCached(const char *url); + bool DecideIfConsideredCached(long long file_size, long long bytes_on_disk); + void WriteFileSizeXAttr(int cinfo_fd, long long file_size); + long long DetermineFullFileSize(const std::string &cinfo_fname); + //-------------------------------------------------------------------- //! \brief Makes decision if the original XrdOucCacheIO should be cached. //! @@ -409,6 +413,7 @@ private: bool xcschk(XrdOucStream &); bool xdlib(XrdOucStream &); bool xtrace(XrdOucStream &); + bool test_oss_basics_and_features(); bool cfg2bytes(const std::string &str, long long &store, long long totalSpace, const char *name); @@ -437,6 +442,8 @@ private: int m_RAM_std_size; bool m_isClient; //!< True if running as client + bool m_dataXattr = false; //!< True if xattrs are available on the data space + bool m_metaXattr = false; //!< True if xattrs are available on the meta space struct WriteQ { diff --git a/src/XrdPfc/XrdPfcConfiguration.cc b/src/XrdPfc/XrdPfcConfiguration.cc index 075b3dbf7d9..74e4a098626 100644 --- a/src/XrdPfc/XrdPfcConfiguration.cc +++ b/src/XrdPfc/XrdPfcConfiguration.cc @@ -10,11 +10,14 @@ #include "XrdOuc/XrdOucPinLoader.hh" #include "XrdOuc/XrdOuca2x.hh" -#include "XrdOfs/XrdOfsConfigPI.hh" #include "XrdVersion.hh" +#include "XrdOfs/XrdOfsConfigPI.hh" +#include "XrdSys/XrdSysXAttr.hh" #include +extern XrdSysXAttr *XrdSysXAttrActive; + namespace XrdPfc { const char *trace_what_strings[] = {"","error ","warning ","info ","debug ","dump "}; @@ -268,6 +271,76 @@ bool Cache::xtrace(XrdOucStream &Config) return false; } +// Determine if oss spaces are operational and if they support xattrs. +bool Cache::test_oss_basics_and_features() +{ + static const char *epfx = "test_oss_basics_and_features()"; + + const auto &conf = m_configuration; + const char *user = conf.m_username.c_str(); + XrdOucEnv env; + + auto check_space = [&](const char *space, bool &has_xattr) + { + std::string fname("__prerun_test_pfc_"); + fname += space; + fname += "_space__"; + env.Put("oss.cgroup", space); + + int res = m_oss->Create(user, fname.c_str(), 0600, env, XRDOSS_mkpath); + if (res != XrdOssOK) { + m_log.Emsg(epfx, "Can not create a file on space", space); + return false; + } + XrdOssDF *oss_file = m_oss->newFile(user); + res = oss_file->Open(fname.c_str(), O_RDWR, 0600, env); + if (res != XrdOssOK) { + m_log.Emsg(epfx, "Can not open a file on space", space); + return false; + } + res = oss_file->Write(fname.data(), 0, fname.length()); + if (res != (int) fname.length()) { + m_log.Emsg(epfx, "Can not write into a file on space", space); + return false; + } + + has_xattr = true; + long long fsize = fname.length(); + res = XrdSysXAttrActive->Set("pfc.fsize", &fsize, sizeof(long long), 0, oss_file->getFD(), 0); + if (res != 0) { + m_log.Emsg(epfx, "Can not write xattr to a file on space", space); + has_xattr = false; + } + + oss_file->Close(); + + if (has_xattr) { + char pfn[4096]; + m_oss->Lfn2Pfn(fname.c_str(), pfn, 4096); + fsize = -1ll; + res = XrdSysXAttrActive->Get("pfc.fsize", &fsize, sizeof(long long), pfn); + if (res != sizeof(long long) || fsize != (long long) fname.length()) + { + m_log.Emsg(epfx, "Can not read xattr from a file on space", space); + has_xattr = false; + } + } + + res = m_oss->Unlink(fname.c_str()); + if (res != XrdOssOK) { + m_log.Emsg(epfx, "Can not unlink a file on space", space); + return false; + } + + return true; + }; + + bool aOK = true; + aOK &= check_space(conf.m_data_space.c_str(), m_dataXattr); + aOK &= check_space(conf.m_meta_space.c_str(), m_metaXattr); + + return aOK; +} //______________________________________________________________________________ /* Function: Config @@ -384,6 +457,9 @@ bool Cache::Config(const char *config_filename, const char *parameters) return false; } + // Test if OSS is operational, determine optional features. + aOK &= test_oss_basics_and_features(); + // sets default value for disk usage XrdOssVSInfo sP; { @@ -428,6 +504,7 @@ bool Cache::Config(const char *config_filename, const char *parameters) else aOK = false; } } + // sets flush frequency if ( ! tmpc.m_flushRaw.empty()) { @@ -461,7 +538,6 @@ bool Cache::Config(const char *config_filename, const char *parameters) } // Setup number of standard-size blocks not released back to the system to 5% of total RAM. m_configuration.m_RamKeepStdBlocks = (m_configuration.m_RamAbsAvailable / m_configuration.m_bufferSize + 1) * 5 / 100; - // Set tracing to debug if this is set in environment char* cenv = getenv("XRDDEBUG"); diff --git a/src/XrdPfc/XrdPfcFile.cc b/src/XrdPfc/XrdPfcFile.cc index d2db3337693..f96eae37592 100644 --- a/src/XrdPfc/XrdPfcFile.cc +++ b/src/XrdPfc/XrdPfcFile.cc @@ -474,6 +474,7 @@ bool File::Open() m_cfi.ResetNoCkSumTime(); m_cfi.Write(m_info_file, ifn.c_str()); m_info_file->Fsync(); + cache()->WriteFileSizeXAttr(m_info_file->getFD(), m_file_size); TRACEF(Debug, tpfx << "Creating new file info, data size = " << m_file_size << " num blocks = " << m_cfi.GetNBlocks()); } @@ -487,6 +488,27 @@ bool File::Open() return true; } +int File::Fstat(struct stat &sbuff) +{ + // Stat on an open file. + // Corrects size to actual full size of the file. + // Sets atime to 0 if the file is only partially downloaded, in accordance + // with pfc.onlyifcached settings. + // Called from IO::Fstat() and Cache::Stat() when the file is active. + // Returns 0 on success, -errno on error. + + int res; + + if ((res = m_data_file->Fstat(&sbuff))) return res; + + sbuff.st_size = m_file_size; + + bool is_cached = cache()->DecideIfConsideredCached(m_file_size, sbuff.st_blocks * 512ll); + if ( ! is_cached) + sbuff.st_atime = 0; + + return 0; +} //============================================================================== // Read and helpers diff --git a/src/XrdPfc/XrdPfcFile.hh b/src/XrdPfc/XrdPfcFile.hh index d1d0672cb1c..58d10d987cf 100644 --- a/src/XrdPfc/XrdPfcFile.hh +++ b/src/XrdPfc/XrdPfcFile.hh @@ -295,6 +295,8 @@ public: long long GetPrefetchedBytes() const { return m_prefetch_bytes; } const Stats& RefStats() const { return m_stats; } + int Fstat(struct stat &sbuff); + // These three methods are called under Cache's m_active lock int get_ref_cnt() { return m_ref_cnt; } int inc_ref_cnt() { return ++m_ref_cnt; } diff --git a/src/XrdPfc/XrdPfcIOFile.cc b/src/XrdPfc/XrdPfcIOFile.cc index 1a54db38021..3c39966d72d 100644 --- a/src/XrdPfc/XrdPfcIOFile.cc +++ b/src/XrdPfc/XrdPfcIOFile.cc @@ -35,8 +35,7 @@ using namespace XrdPfc; //______________________________________________________________________________ IOFile::IOFile(XrdOucCacheIO *io, Cache & cache) : IO(io, cache), - m_file(0), - m_localStat(0) + m_file(0) { m_file = Cache::GetInstance().GetFile(GetFilename(), this); } @@ -47,22 +46,20 @@ IOFile::~IOFile() // called from Detach() if no sync is needed or // from Cache's sync thread TRACEIO(Debug, "~IOFile() " << this); - - delete m_localStat; } //______________________________________________________________________________ int IOFile::Fstat(struct stat &sbuff) { - int res = 0; - if( ! m_localStat) - { - res = initCachedStat(); - if (res) return res; + // This is only called during Cache::Attach / Cache::GetFile() for file creation. + // Should really be separate name but one needs to change virtual interface + // so initialStat() becomes virtual in the base-class. + // Also, IOFileBlock should be ditched. + if ( ! m_file) { + return initialStat(sbuff); } - memcpy(&sbuff, m_localStat, sizeof(struct stat)); - return 0; + return m_file->Fstat(sbuff); } //______________________________________________________________________________ @@ -72,65 +69,30 @@ long long IOFile::FSize() } //______________________________________________________________________________ -int IOFile::initCachedStat() +int IOFile::initialStat(struct stat &sbuff) { - // Called indirectly from the constructor. - - static const char* trace_pfx = "initCachedStat "; + // Get stat to determine file-size. + // Called indirectly from the constructor, via Cache::GetFile(). + // Either read it from cinfo file or obtain it from the remote IO. - int res = -1; - struct stat tmpStat; + static const char* trace_pfx = "initialStat "; std::string fname = GetFilename(); - std::string iname = fname + Info::s_infoExtension; - if (m_cache.GetOss()->Stat(fname.c_str(), &tmpStat) == XrdOssOK) + if (m_cache.GetOss()->Stat(fname.c_str(), &sbuff) == XrdOssOK) { - XrdOssDF* infoFile = m_cache.GetOss()->newFile(Cache::GetInstance().RefConfiguration().m_username.c_str()); - XrdOucEnv myEnv; - int res_open; - if ((res_open = infoFile->Open(iname.c_str(), O_RDONLY, 0600, myEnv)) == XrdOssOK) + long long file_size = m_cache.DetermineFullFileSize(fname + Info::s_infoExtension); + if (file_size >= 0) { - Info info(m_cache.GetTrace()); - if (info.Read(infoFile, iname.c_str())) - { - // The filesize from the file itself may be misleading if its download is incomplete; take it from the cinfo. - tmpStat.st_size = info.GetFileSize(); - // We are arguably abusing the mtime to be the creation time of the file; then ctime becomes the - // last time additional data was cached. - tmpStat.st_mtime = info.GetCreationTime(); - TRACEIO(Info, trace_pfx << "successfully read size " << tmpStat.st_size << " and creation time " << tmpStat.st_mtime << " from info file"); - res = 0; - } - else - { - // file exist but can't read it - TRACEIO(Info, trace_pfx << "info file is incomplete or corrupt"); - } + sbuff.st_size = file_size; + TRACEIO(Info, trace_pfx << "successfully read size " << sbuff.st_size << " from info file"); + return 0; } - else - { - TRACEIO(Error, trace_pfx << "can't open info file " << XrdSysE2T(-res_open)); - } - infoFile->Close(); - delete infoFile; + TRACEIO(Error, trace_pfx << "failed reading from info file " << XrdSysE2T(-file_size)); } - if (res) - { - res = GetInput()->Fstat(tmpStat); - TRACEIO(Debug, trace_pfx << "got stat from client res = " << res << ", size = " << tmpStat.st_size); - // The mtime / atime / ctime for cached responses come from the file on disk in the cache hit case. - // To avoid weirdness when two subsequent stat queries can give wildly divergent times (one from the - // origin, one from the cache), set the times to "now" so we effectively only report the *time as the - // cache service sees it. - tmpStat.st_ctime = tmpStat.st_mtime = tmpStat.st_atime = time(NULL); - } + int res = GetInput()->Fstat(sbuff); + TRACEIO(Debug, trace_pfx << "stat from client res = " << res << ", size = " << sbuff.st_size); - if (res == 0) - { - m_localStat = new struct stat; - memcpy(m_localStat, &tmpStat, sizeof(struct stat)); - } return res; } diff --git a/src/XrdPfc/XrdPfcIOFile.hh b/src/XrdPfc/XrdPfcIOFile.hh index b63941b8974..f1dc0e036f1 100644 --- a/src/XrdPfc/XrdPfcIOFile.hh +++ b/src/XrdPfc/XrdPfcIOFile.hh @@ -86,8 +86,7 @@ private: int ReadVBegin(const XrdOucIOVec *readV, int n, ReadReqRH *rh); int ReadVEnd(int retval, ReadReqRH *rh); - struct stat *m_localStat; - int initCachedStat(); + int initialStat(struct stat &sbuff); }; } From 075f17dd69dce43a89d63221577d332d6abe429f Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Wed, 6 Nov 2024 16:53:16 +0100 Subject: [PATCH 236/276] [XrdOuc] Migrate obfuscateAuth() away from std::regex Use POSIX regex functions instead of std::regex, as the latter is unusable due to the bug below. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86164 --- src/XrdOuc/XrdOucUtils.cc | 83 +++++++++++++++++++++++++-------------- src/XrdOuc/XrdOucUtils.hh | 1 - 2 files changed, 54 insertions(+), 30 deletions(-) diff --git a/src/XrdOuc/XrdOucUtils.cc b/src/XrdOuc/XrdOucUtils.cc index ef111340d64..40d4e811f28 100644 --- a/src/XrdOuc/XrdOucUtils.cc +++ b/src/XrdOuc/XrdOucUtils.cc @@ -36,6 +36,8 @@ #include #include +#include + #ifdef WIN32 #include #include "XrdSys/XrdWin32.hh" @@ -115,19 +117,6 @@ int LookUp(idMap_t &idMap, unsigned int id, char *buff, int blen) } } -static const std::string OBFUSCATION_STR = "REDACTED"; - -// As the compilation of the regexes when the std::regex object is constructed is expensive, -// we initialize the auth obfuscation regexes only once in the XRootD process lifetime - -static const std::vector authObfuscationRegexes = { - //authz=xxx&... We deal with cases like "(message: kXR_stat (path: /tmp/xrootd/public/foo?pelican.timeout=3s&authz=foo1234, flags: none)" where we do not want to obfuscate - // ", flags: none)" + we deal with cases where the 'authz=Bearer token' when an admin could set 'http.header2cgi Authorization authz' in the server config - std::regex(R"((authz=)(Bearer(\s|%20))?([^ &\",<>#%{}|\^~\[\]`]*))",std::regex_constants::optimize), - // HTTP Authorization, TransferHeaderAuthorization headers that with the key that can be prefixed with spaces and value prefixed by spaces - std::regex(R"((\s*\w*Authorization\s*:\s*)[^$]*)", std::regex_constants::icase | std::regex_constants::optimize) -}; - /******************************************************************************/ /* a r g L i s t */ /******************************************************************************/ @@ -1429,26 +1418,62 @@ void XrdOucUtils::trim(std::string &str) { } /** - * Use this function to obfuscate any string containing key-values with OBFUSCATION_STR + * Returns a boolean indicating whether 'c' is a valid token character or not. + * See https://datatracker.ietf.org/doc/html/rfc6750#section-2.1 for details. + */ + +static bool is_token_character(int c) +{ + if (isalnum(c)) + return true; + + static constexpr char token_chars[] = "-._~+/=:"; + + for (char ch : token_chars) + if (c == ch) + return true; + + return false; +} + +/** + * This function obfuscates away authz= cgi elements and/or HTTP authorization + * headers from URL or other log line strings which might contain them. + * * @param input the string to obfuscate - * @param regexes the obfuscation regexes to apply to replace the value with OBFUSCATION_STR. - * The key should be a regex group e.g: "(authz=)" - * Have a look at obfuscateAuth for more examples - * @return the string with values obfuscated + * @return the string with token values obfuscated */ -std::string obfuscate(const std::string &input, const std::vector ®exes) { - std::string result = input; - for(const auto & regex: regexes) { - //Loop over the regexes and replace the values with OBFUSCATION_STR - //$1 matches the first regex subgroup (e.g: "(authz=)") - result = std::regex_replace(result, regex, std::string("$1" + OBFUSCATION_STR)); + +std::string obfuscateAuth(const std::string& input) +{ + static const regex_t auth_regex = []() { + constexpr char re[] = + "(authz=|(transferheader)?(www-|proxy-)?auth(orization|enticate)[[:space:]]*:[[:space:]]*)" + "(Bearer([[:space:]]|%20)?(token([[:space:]]|%20)?)?)?"; + + regex_t regex; + + if (regcomp(®ex, re, REG_EXTENDED | REG_ICASE) != 0) + throw std::runtime_error("Failed to compile regular expression"); + + return regex; + }(); + + regmatch_t match; + size_t offset = 0; + std::string redacted; + const char *const text = input.c_str(); + + while (regexec(&auth_regex, text + offset, 1, &match, 0) == 0) { + redacted.append(text + offset, match.rm_eo).append("REDACTED"); + + offset += match.rm_eo; + + while (offset < input.size() && is_token_character(input[offset])) + ++offset; } - return result; -} -std::string obfuscateAuth(const std::string & input) { - return obfuscate(input, authObfuscationRegexes); + return redacted.append(text + offset); } #endif - diff --git a/src/XrdOuc/XrdOucUtils.hh b/src/XrdOuc/XrdOucUtils.hh index 8b612614599..265ddde26aa 100644 --- a/src/XrdOuc/XrdOucUtils.hh +++ b/src/XrdOuc/XrdOucUtils.hh @@ -35,7 +35,6 @@ #include #include #include -#include class XrdSysError; class XrdOucString; From caf46d216d0a219c228e270c8287a7d72387e224 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Wed, 6 Nov 2024 15:55:01 +0100 Subject: [PATCH 237/276] [Tests] Make test for obfuscateAuth() stricter This changes the test to try all token values with all test strings, adds tests with strings with surrounding quotes around the token value, and adds a very long token value for testing with the test strings. --- tests/XrdOucTests/XrdOucUtilsTests.cc | 274 ++++++++++++++++++++++---- 1 file changed, 237 insertions(+), 37 deletions(-) diff --git a/tests/XrdOucTests/XrdOucUtilsTests.cc b/tests/XrdOucTests/XrdOucUtilsTests.cc index 0242a39c07f..3deafc95e58 100644 --- a/tests/XrdOucTests/XrdOucUtilsTests.cc +++ b/tests/XrdOucTests/XrdOucUtilsTests.cc @@ -1,47 +1,247 @@ #undef NDEBUG -#include -#include -#include - #include "XrdOuc/XrdOucUtils.hh" - #include "XrdOuc/XrdOucTUtils.hh" #include "XrdOuc/XrdOucPrivateUtils.hh" +#include +#include + +#include + +class XrdOucUtilsTests : public ::testing::Test {}; + +/* String we replace token values with */ +static const std::string redacted = "REDACTED"; + +/* + * These checks are meant to ensure tokens as described in sections below are + * not leaked into the output of either the server or the client in log lines. + * + * Access Token Types: https://datatracker.ietf.org/doc/html/rfc6749#section-7.1 + * Authorization Header: https://datatracker.ietf.org/doc/html/rfc6750#section-2.1 + * + */ + +static const std::string token_prefixes[] = { + "", + "Bearer", + "Bearer ", + "Bearer%20", + "Bearertoken", + "Bearertoken ", + "Bearer token", + "Bearer token ", + "Bearer%20token", + "Bearer%20token%20", +}; + +static const std::string tokens[] = { + /* short tokens */ + "my_secret_token", + "my.secret.token/with~special+chars==", + + /* macaroon */ + "MDAxY2xvY2F0aW9uIE9wdGlvbmFsLmVtcHR5CjAwMThpZGVudGlmaWVyIGh" + "sQ0kremlRCjAwMTVjaWQgaWlkOnBGTTA1MnJTCjAwMjFjaWQgaWQ6MjAwMj" + "sxMDAxLDIwMDIsMDtwzXVsCjAwMjhjaWQgYmVmb3JlOjIwMTktMDQtMTdUM" + "Dk6NTE6MjIuODQwWgowMDE5Y2lkIGhvbWU6L1VzZXJzL3BhdWwKMDAyZnNp" + "Z25hdHVyZSCT6Lea6oBIEpiF2KOsZ1FQvLeoXve_a3q38TZTBWhM1Qo", + + /* eos token */ + "zteos64:MDAwMDAyMGN4nON6z8jFXFReIbBj16edbBqMRmL6qfnF-snJiWlplfoZJSUF8SWpxSX6F" + "oxeKqlmhiapqWZGuonGpkm6hoapaboW5iaWummJhmbGqaamKZaJqUHzGaPdijIV_PLLFAxNgcjK0M" + "TK0EzByMDIJFahNDPFyiC6KD-_JFYhHYldkpmSmldiBeLoGRuYmBobW1iZGJs65OQnJ-Zk5BeXKOQ" + "l5qaC5RVS8qwUCoryS6xK8zIrFBILCqwUQCqsEGpT8nMTM_MgAhC2QnpqvpVCcWlKvpWhkIKUmL3V" + "4wPOwTNXqXcybNGTYjp9eOrjQr", + + /* demo scitoken */ + "eyJhbGciOiJSUzI1NiIsImtpZCI6ImtleS1yczI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXIiOiJzY2l0" + "b2tlbjoyLjAiLCJhdWQiOiJodHRwczovL2RlbW8uc2NpdG9rZW5zLm9yZyIsImlzcyI6Imh0dHBzO" + "i8vZGVtby5zY2l0b2tlbnMub3JnIiwiZXhwIjoxNzMwODk3NTU1LCJpYXQiOjE3MzA4OTY5NTUsIm" + "5iZiI6MTczMDg5Njk1NSwianRpIjoiYzUxOTM0OWEtMzRlMi00MTg2LTljMTMtNDU3Njk1ZjQwNTk" + "3In0.q0aLuqK8BpI7FqPw7VJYym2B3SLyYiU_7xH_y_dD-jmdOUuH8pySgvsCzlrKcqgVY6-E8ggq" + "fM09HqAMJCe5MRiOpZj34D8zSU3kgTC8bh9fjy6sYgTwnmzkCGXO5xdf_H7Xw1VO2eVOPJUHtsmc7" + "pa6_geLmHiJvSthKgd9XceRyQ5R8q9T5E03LsAmks4rhTC1dJaCGB2EUguKxXhos2dBk09MhPQOB7" + "jvQKFPXu9tdJb7eWNMPETxnWTJF7kn5zKIs1by2bcHtpdEpOIQ3qfGZhThzUeZ9NZC0FXsyKhoKoJ" + "EAkevGtNbs72NqZr3scxVUj_zHK6QIWe2UI7dKg", + + /* malicious "tokens" */ + std::string(65536, 'X'), + std::string(65536, '.'), +}; + +static const std::string plain_urls[] = { + /* empty URL */ + "", + + /* some realistic URLs */ + "root://eos.cern.ch//eos/", + "https://my.cdash.org/index.php?project=XRootD", + "https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86164", + "https://zoom.us/j/6215903872?pwd=MzErQXJNXXXXX.RsS2VLSVgzVmtrdz09&omn=62242940660#success", + "https://p0@F4HP7QL65F.local:61631//first/namespace/token_gen/test1171569942?&timeout=9.5s", + "root://localhost:10940//data/cb4aacf1-6f28-42f2-b68a-90a73460f424.dat?xrdcl.requuid=26dab270-6d8a-43b1-8ebc-befb372c0d60", + + /* malicious "URLs" */ + std::string(65536, 'A'), + std::string(65536, '.'), + std::string(65536, '&'), + std::string(65536, ' '), +}; + +static const std::string authz_strings[] = { + "authz=REDACTED", + " authz=REDACTED ", + " 'authz=REDACTED' ", + " \"authz=REDACTED\" ", + "authz=REDACTED&scitag.flow=144&test=abcd", + "scitag.flow=144&authz=REDACTED&test=abcd", + "scitag.flow=144&test=abcd&authz=REDACTED", + "authz=REDACTED&test=test2&authz=REDACTED", + "authz=REDACTED&test=test2&authz=REDACTED&authz=REDACTED&test=test2&authz=REDACTED", + "/path/test.txt?scitag.flow=44&authz=REDACTED done close.", + "/path/test.txt?authz=REDACTED&scitag.flow=44 done close.", + "(message: kXR_stat (path: /tmp/xrootd/public/foo?authz=REDACTED&pelican.timeout=3s, flags: none) ).", + "(message: kXR_stat (path: /tmp/xrootd/public/foo?pelican.timeout=3s&authz=REDACTED, flags: none) ).", + "Appended header field to opaque info: 'authz=REDACTED'", + "Processing source entry: /etc/passwd, target file: root://localhost:1094//tmp/passwd?authz=REDACTED", + "240919 08:11:07 20995 unknown.3:33@[::1] Pss_Stat: url=pelican://p0@F4HP7QL65F.local:" /* no comma! */ + "61631//first/namespace/token_gen/test1171569942?&authz=REDACTED&pelican.timeout=9.5s" +}; + +static const std::string authz_headers[] = { + "authorization:REDACTED", + "authorization :REDACTED", + "authorization : REDACTED", + "Authorization:REDACTED", + "Authorization: REDACTED", + "Authorization :REDACTED", + "Authorization : REDACTED", + "transferHeaderauthorization: REDACTED", + "transferHeaderauthorization :REDACTED", + "transferHeaderauthorization : REDACTED", + "TransferHeaderAuthorization: REDACTED", + "TransferHeaderAuthorization :REDACTED", + "TransferHeaderAuthorization : REDACTED", + "WWW-Authenticate: REDACTED", + "Proxy-Authenticate: REDACTED", + "WWW-Authenticate : REDACTED", + "Proxy-Authenticate : REDACTED", +}; + +/* Check that plain URLs not containing a token remain intact */ + +TEST(XrdOucUtilsTests, RedactToken_PlainURLs) +{ + for (std::string str : plain_urls) + ASSERT_EQ(str, obfuscateAuth(str)); +} + +/* Check URLs with an empty token as a special case. This is needed + * because in the case an empty token is provided with prefix "Bearer ", + * that is, containing a space at the end, a word from the actual output + * will be redacted as if it were the value of the token. + * + * Example: + * "/test.txt?scitag.flow=44&authz=Bearer done close." + * Becomes: + * "/test.txt?scitag.flow=44&authz=REDACTED close." + * + * In this special case, whenever the prefix ends with a space, we check + * only that the word "REDACTED" appears in the output. + */ + +TEST(XrdOucUtilsTests, RedactToken_AuthzCGI_EmptyToken) +{ + for (std::string authz : authz_strings) { + for (std::string prefix : token_prefixes) { + std::string str = authz; + + size_t pos = 0; + while ((pos = str.find(redacted, pos)) != std::string::npos) + str = str.replace(pos, redacted.size(), prefix); + + std::string obfuscated_str = obfuscateAuth(str); + + /* Assert that we do find the word "REDACTED" in the output */ + ASSERT_TRUE(obfuscated_str.find(redacted) != std::string::npos); + + /* Skip input/output equality check since when prefix ends with a + * space, or input contains "REDACTED " with a space, an extra word + * will be consumed as if it were the token value. */ + } + } +} + +TEST(XrdOucUtilsTests, RedactToken_AuthzCGI_ValidToken) +{ + size_t pos = 0; + for (std::string authz : authz_strings) { + for (std::string prefix : token_prefixes) { + for (std::string token : tokens) { + std::string str = authz; + + pos = 0; + /* Replace all "REDACTED" strings with a token value in the test string */ + while ((pos = str.find(redacted, pos)) != std::string::npos) + str = str.replace(pos, redacted.size(), prefix + token); + + /* Call obfuscateAuth(str) to redact token values */ + std::string obfuscated_str = obfuscateAuth(str); + + pos = 0; + /* Replace all token values back with "REDACTED" in the test string */ + while ((pos = str.find(token, pos)) != std::string::npos) + str = str.replace(pos, token.size(), redacted); -using namespace testing; - -// duplicated here to avoid becoming a public symbol of XrdUtils -static const std::string OBFUSCATION_STR = "REDACTED"; - -class XrdOucUtilsTests : public Test {}; - -TEST(XrdOucUtilsTests, obfuscateAuth) { - // General cases - ASSERT_EQ(std::string("scitag.flow=144&authz=") + OBFUSCATION_STR + std::string("&test=abcd"), obfuscateAuth("scitag.flow=144&authz=token&test=abcd")); - ASSERT_EQ(std::string("authz=") + OBFUSCATION_STR + std::string("&scitag.flow=144&test=abcd"), obfuscateAuth("authz=token&scitag.flow=144&test=abcd")); - ASSERT_EQ(std::string("scitag.flow=144&test=abcd&authz=") + OBFUSCATION_STR, obfuscateAuth("scitag.flow=144&test=abcd&authz=token")); - // Nothing to obfuscate - ASSERT_EQ("test=abcd&test2=abcde",obfuscateAuth("test=abcd&test2=abcde")); - ASSERT_EQ("nothingtoobfuscate",obfuscateAuth("nothingtoobfuscate")); - //Empty string obfuscation - ASSERT_EQ("",obfuscateAuth("")); - //2 authz to obfuscate - ASSERT_EQ(std::string("authz=") + OBFUSCATION_STR + std::string("&test=test2&authz=") + OBFUSCATION_STR,obfuscateAuth("authz=abcd&test=test2&authz=abcdef")); - // Trimmed key obfuscation - ASSERT_EQ(std::string("Authorization: ") + OBFUSCATION_STR, obfuscateAuth("Authorization: Bearer token")); - ASSERT_EQ(std::string("Authorization :") + OBFUSCATION_STR, obfuscateAuth("Authorization :Bearer token")); - ASSERT_EQ(std::string("authorization :") + OBFUSCATION_STR, obfuscateAuth("authorization :Bearer token")); - ASSERT_EQ(std::string("transferHeaderauthorization :") + OBFUSCATION_STR, obfuscateAuth("transferHeaderauthorization :Bearer token")); - // Different obfuscation - ASSERT_EQ(std::string("(message: kXR_stat (path: /tmp/xrootd/public/foo?authz=") + OBFUSCATION_STR + std::string("&pelican.timeout=3s, flags: none) )."), obfuscateAuth("(message: kXR_stat (path: /tmp/xrootd/public/foo?authz=foo1234&pelican.timeout=3s, flags: none) ).")); - ASSERT_EQ(std::string("(message: kXR_stat (path: /tmp/xrootd/public/foo?pelican.timeout=3s&authz=") + OBFUSCATION_STR + std::string(", flags: none) )."), obfuscateAuth("(message: kXR_stat (path: /tmp/xrootd/public/foo?pelican.timeout=3s&authz=foo1234, flags: none) ).")); - ASSERT_EQ(std::string("/path/test.txt?scitag.flow=44&authz=") + OBFUSCATION_STR + std::string(" done close."),obfuscateAuth("/path/test.txt?scitag.flow=44&authz=abcdef done close.")); - ASSERT_EQ(std::string("Appended header field to opaque info: 'authz=") + OBFUSCATION_STR, obfuscateAuth("Appended header field to opaque info: 'authz=Bearer abcdef'")); - ASSERT_EQ(std::string("Appended header fields to opaque info: 'authz=") + OBFUSCATION_STR + std::string("&scitag.flow=65'"), obfuscateAuth("Appended header fields to opaque info: 'authz=Bearer token&scitag.flow=65'")); - ASSERT_EQ(std::string("Processing source entry: /etc/passwd, type local file, target file: root://localhost:1094//tmp/passwd?authz=") + OBFUSCATION_STR, obfuscateAuth("Processing source entry: /etc/passwd, type local file, target file: root://localhost:1094//tmp/passwd?authz=testabcd")); - ASSERT_EQ(std::string("240919 08:11:07 20995 unknown.3:33@[::1] Pss_Stat: url=pelican://p0@F4HP7QL65F.local:61631//first/namespace/token_gen/test1171569942?&authz=") + OBFUSCATION_STR + std::string("&pelican.timeout=9.5s"),obfuscateAuth("240919 08:11:07 20995 unknown.3:33@[::1] Pss_Stat: url=pelican://p0@F4HP7QL65F.local:61631//first/namespace/token_gen/test1171569942?&authz=Bearer%20tokentoberedacted&pelican.timeout=9.5s")); + /* Assert that we do not find the token value in the output */ + ASSERT_TRUE(obfuscated_str.find(token) == std::string::npos) + << "\ntoken = '" << token << "'\n str = '" << obfuscated_str << "'" << std::endl; + + /* Assert that we do find the word "REDACTED" in the output */ + ASSERT_TRUE(obfuscated_str.find(redacted) != std::string::npos); + + /* Assert that we get back the original string after redaction */ + ASSERT_EQ(str, obfuscated_str); + } + } + } +} + +TEST(XrdOucUtilsTests, RedactToken_AuthHeader) +{ + size_t pos = 0; + for (std::string header : authz_headers) { + for (std::string prefix : token_prefixes) { + for (std::string token : tokens) { + std::string str = header; + + pos = 0; + /* replace all "REDACTED" strings with a token value in the test string */ + while ((pos = str.find(redacted, pos)) != std::string::npos) + str = str.replace(pos, redacted.size(), prefix + token); + + /* Call obfuscateAuth(str) to redact token values */ + std::string obfuscated_str = obfuscateAuth(str); + + pos = 0; + /* Replace all token values back with "REDACTED" in the test string */ + while ((pos = str.find(token, pos)) != std::string::npos) + str = str.replace(pos, token.size(), redacted); + + /* Assert that we do not find the token value in the output */ + ASSERT_TRUE(obfuscated_str.find(token) == std::string::npos) + << "\ntoken = '" << token << "'\n str = '" << obfuscated_str << "'" << std::endl; + + /* Assert that we do find the word "REDACTED" in the output */ + ASSERT_TRUE(obfuscated_str.find(redacted) != std::string::npos); + + /* Assert that we get back the original string after redaction */ + ASSERT_EQ(str, obfuscated_str); + } + } + } } TEST(XrdOucUtilsTests, caseInsensitiveFind) { From 216097d48054d1c9d71f85f6d56df561c317979d Mon Sep 17 00:00:00 2001 From: Matevz Tadel Date: Fri, 22 Nov 2024 15:37:02 -0800 Subject: [PATCH 238/276] [pfc-2372-3] Return 1 from Stat when the cache does not have the file -- let posix layer stat the remote. To be extended to support only-if-cached case later (through the fsctl API). --- src/XrdPfc/XrdPfc.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/XrdPfc/XrdPfc.cc b/src/XrdPfc/XrdPfc.cc index 7c7a09c74b0..567bda0d9a2 100644 --- a/src/XrdPfc/XrdPfc.cc +++ b/src/XrdPfc/XrdPfc.cc @@ -1128,7 +1128,7 @@ int Cache::Stat(const char *curl, struct stat &sbuff) int res = m_oss->Stat(f_name.c_str(), &sbuff); if (res != XrdOssOK) { TRACE(Debug, tpfx << curl << " -> " << res); - return res; + return 1; // res; -- for only-if-cached } if (S_ISDIR(sbuff.st_mode)) { @@ -1139,7 +1139,7 @@ int Cache::Stat(const char *curl, struct stat &sbuff) long long file_size = DetermineFullFileSize(f_name + Info::s_infoExtension); if (file_size < 0) { TRACE(Debug, tpfx << curl << " -> " << file_size); - return (int) file_size; + return 1; // (int) file_size; -- for only-if-cached } sbuff.st_size = file_size; bool is_cached = DecideIfConsideredCached(file_size, sbuff.st_blocks * 512ll); From 50209b3a5ed361f0e2c8541632cea1e217035b39 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Mon, 25 Nov 2024 11:45:38 +0100 Subject: [PATCH 239/276] [Secsss] Fix upcast array used in pointer arithmetics --- src/XrdSecsss/XrdSecProtocolsss.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/XrdSecsss/XrdSecProtocolsss.cc b/src/XrdSecsss/XrdSecProtocolsss.cc index 28466cab72e..5fc13566d5f 100644 --- a/src/XrdSecsss/XrdSecProtocolsss.cc +++ b/src/XrdSecsss/XrdSecProtocolsss.cc @@ -590,7 +590,7 @@ int XrdSecProtocolsss::getCred(XrdOucErrInfo *einfo, // Extract out the loginid. This messy code is for backwards compatibility. // - bP = prData.Data; eodP = dLen + (char *)&prData; + bP = prData.Data; eodP = prData.Data + dLen; while(bP < eodP) {idType = *bP++; if (!XrdOucPup::Unpack(&bP, eodP, &idP, idSz) || !idP || *idP == 0) From a1bcd4426b6930a91aea29169be69caedb82dbe6 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Mon, 25 Nov 2024 13:39:01 +0100 Subject: [PATCH 240/276] [Server] Fix uncontrolled format string in XrdXrootdTpcMon The format string in urlFMT is built from strings which come from the environment, so one could pass in malicious strings that would alter the actual format being used in XrdXrootdTpcMon::getURL(). --- src/XrdXrootd/XrdXrootdTpcMon.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/XrdXrootd/XrdXrootdTpcMon.cc b/src/XrdXrootd/XrdXrootdTpcMon.cc index e95ec709795..05737dc272a 100644 --- a/src/XrdXrootd/XrdXrootdTpcMon.cc +++ b/src/XrdXrootd/XrdXrootdTpcMon.cc @@ -46,7 +46,7 @@ const char *json_fmt = "{\"TPC\":\"%s\",\"Client\":\"%s\"," "\"IPv\":%c}," "\"Src\":\"%s\",\"Dst\":\"%s\",\"Size\":%zu}"; -const char *urlFMT = ""; +const char *hostport = ""; XrdSysError eDest(0, "Ouc"); } @@ -72,8 +72,8 @@ XrdXrootdTpcMon::XrdXrootdTpcMon(const char *proto, const char *host = getenv("XRDHOST"); if (!host) host = "localhost"; const char *port = getenv("XRDPORT"); if (!port) {colon = ""; port = "";} - snprintf(buff, sizeof(buff), "%%s://%s%s%s/%%s", host, colon, port); - urlFMT = strdup(buff); + snprintf(buff, sizeof(buff), "%s%s%s", host, colon, port); + hostport = strdup(buff); } /******************************************************************************/ @@ -86,7 +86,7 @@ const char *XrdXrootdTpcMon::getURL(const char *spec, const char *prot, // Handle the spec // if (*spec == '/') - {snprintf(buff, bsz, urlFMT, prot, spec); + {snprintf(buff, bsz, "%s://%s/%s", prot, hostport, spec); spec = buff; } return spec; From bb6c75b6de05d0844d1a19d35c352658a36ed625 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Mon, 25 Nov 2024 13:33:51 +0100 Subject: [PATCH 241/276] [XrdCl] Fix wrong type of arguments to formatting function - Use %llu instead of %d for size_t - Use %p format for pointer types instead of 0x%x --- src/XrdCl/XrdClAsyncMsgReader.hh | 8 +- src/XrdCl/XrdClAsyncMsgWriter.hh | 4 +- src/XrdCl/XrdClCopyProcess.cc | 2 +- src/XrdCl/XrdClFileStateHandler.cc | 157 ++++++++++++++--------------- src/XrdCl/XrdClFileSystem.cc | 14 +-- src/XrdCl/XrdClInQueue.cc | 6 +- src/XrdCl/XrdClJobManager.cc | 2 +- src/XrdCl/XrdClPollerBuiltIn.cc | 2 +- src/XrdCl/XrdClSocket.cc | 2 +- src/XrdCl/XrdClStream.cc | 20 ++-- src/XrdCl/XrdClUtils.cc | 2 +- src/XrdCl/XrdClXRootDMsgHandler.cc | 18 ++-- src/XrdCl/XrdClXRootDMsgHandler.hh | 4 +- src/XrdCl/XrdClXRootDTransport.cc | 14 +-- src/XrdCl/XrdClZipArchive.cc | 60 +++++------ 15 files changed, 157 insertions(+), 158 deletions(-) diff --git a/src/XrdCl/XrdClAsyncMsgReader.hh b/src/XrdCl/XrdClAsyncMsgReader.hh index cae92bfeab0..0f4587c39af 100644 --- a/src/XrdCl/XrdClAsyncMsgReader.hh +++ b/src/XrdCl/XrdClAsyncMsgReader.hh @@ -109,14 +109,14 @@ namespace XrdCl if( !st.IsOK() || st.code == suRetry ) return st; - log->Dump( AsyncSockMsg, "[%s] Received message header for 0x%x size: %d", + log->Dump( AsyncSockMsg, "[%s] Received message header for %p size: %d", strmname.c_str(), inmsg.get(), inmsg->GetCursor() ); ServerResponse *rsp = (ServerResponse*)inmsg->GetBuffer(); if( rsp->hdr.status == kXR_attn ) { log->Dump( AsyncSockMsg, "[%s] Will readout the attn action code " - "of message 0x%x", strmname.c_str(), inmsg.get() ); + "of message %p", strmname.c_str(), inmsg.get() ); inmsg->ReAllocate( 16 ); // header (bytes 8) + action code (8 bytes) readstage = ReadAttn; continue; @@ -128,7 +128,7 @@ namespace XrdCl if( inhandler ) { log->Dump( AsyncSockMsg, "[%s] Will use the raw handler to read body " - "of message 0x%x", strmname.c_str(), inmsg.get() ); + "of message %p", strmname.c_str(), inmsg.get() ); //-------------------------------------------------------------- // The next step is to read raw data //-------------------------------------------------------------- @@ -260,7 +260,7 @@ namespace XrdCl //---------------------------------------------------------------- // Report the incoming message //---------------------------------------------------------------- - log->Dump( AsyncSockMsg, "[%s] Received message 0x%x of %d bytes", + log->Dump( AsyncSockMsg, "[%s] Received message %p of %d bytes", strmname.c_str(), inmsg.get(), inmsgsize ); strm.OnIncoming( substrmnb, std::move( inmsg ), inmsgsize ); diff --git a/src/XrdCl/XrdClAsyncMsgWriter.hh b/src/XrdCl/XrdClAsyncMsgWriter.hh index 52458642eb8..edf6199e4c8 100644 --- a/src/XrdCl/XrdClAsyncMsgWriter.hh +++ b/src/XrdCl/XrdClAsyncMsgWriter.hh @@ -161,7 +161,7 @@ namespace XrdCl if( !st.IsOK() || st.code == suRetry ) return st; outmsgsize += wrtcnt; log->Dump( AsyncSockMsg, "[%s] Wrote %d bytes of raw data of message" - "(0x%x) body.", strmname.c_str(), wrtcnt, outmsg ); + "(%p) body.", strmname.c_str(), wrtcnt, outmsg ); } //---------------------------------------------------------------- // The next step is to finalize the write operation @@ -182,7 +182,7 @@ namespace XrdCl return st; } - log->Dump( AsyncSockMsg, "[%s] Successfully sent message: %s (0x%x).", + log->Dump( AsyncSockMsg, "[%s] Successfully sent message: %s (%p).", strmname.c_str(), outmsg->GetObfuscatedDescription().c_str(), outmsg ); strm.OnMessageSent( substrmnb, outmsg, outmsgsize ); diff --git a/src/XrdCl/XrdClCopyProcess.cc b/src/XrdCl/XrdClCopyProcess.cc index c067062b177..72ef3bb52dd 100644 --- a/src/XrdCl/XrdClCopyProcess.cc +++ b/src/XrdCl/XrdClCopyProcess.cc @@ -368,7 +368,7 @@ namespace XrdCl Log *log = DefaultEnv::GetLog(); std::vector::iterator it; - log->Debug( UtilityMsg, "CopyProcess: %d jobs to prepare", + log->Debug( UtilityMsg, "CopyProcess: %llu jobs to prepare", pImpl->pJobProperties.size() ); std::map targetFlags; diff --git a/src/XrdCl/XrdClFileStateHandler.cc b/src/XrdCl/XrdClFileStateHandler.cc index ca11c41d1fe..244fa9b9c19 100644 --- a/src/XrdCl/XrdClFileStateHandler.cc +++ b/src/XrdCl/XrdClFileStateHandler.cc @@ -166,7 +166,7 @@ namespace if( crcval != cksums[pgnb] ) { Log *log = DefaultEnv::GetLog(); - log->Info( FileMsg, "[0x%x@%s] Received corrupted page, will retry page #%d.", + log->Info( FileMsg, "[%p@%s] Received corrupted page, will retry page #%llu.", this, stateHandler->pFileUrl->GetObfuscatedURL().c_str(), pgnb ); XRootDStatus st = XrdCl::FileStateHandler::PgReadRetry( stateHandler, pgoff, pgsize, pgnb, buffer, this, 0 ); @@ -257,7 +257,7 @@ namespace if( !status->IsOK() ) { Log *log = DefaultEnv::GetLog(); - log->Info( FileMsg, "[0x%x@%s] Failed to recover page #%d.", + log->Info( FileMsg, "[%p@%s] Failed to recover page #%llu.", this, pgReadHandler->stateHandler->pFileUrl->GetObfuscatedURL().c_str(), pgnb ); pgReadHandler->HandleResponseWithHosts( status, response, hostList ); delete this; @@ -269,7 +269,7 @@ namespace if( pginf->GetLength() > (uint32_t)XrdSys::PageSize || pginf->GetCksums().size() != 1 ) { Log *log = DefaultEnv::GetLog(); - log->Info( FileMsg, "[0x%x@%s] Failed to recover page #%d.", + log->Info( FileMsg, "[%p@%s] Failed to recover page #%llu.", this, pgReadHandler->stateHandler->pFileUrl->GetObfuscatedURL().c_str(), pgnb ); // we retry a page at a time so the length cannot exceed 4KB DeleteArgs( status, response, hostList ); @@ -282,7 +282,7 @@ namespace if( crcval != pginf->GetCksums().front() ) { Log *log = DefaultEnv::GetLog(); - log->Info( FileMsg, "[0x%x@%s] Failed to recover page #%d.", + log->Info( FileMsg, "[%p@%s] Failed to recover page #%llu.", this, pgReadHandler->stateHandler->pFileUrl->GetObfuscatedURL().c_str(), pgnb ); DeleteArgs( status, response, hostList ); pgReadHandler->HandleResponseWithHosts( new XRootDStatus( stError, errDataError ), 0, 0 ); @@ -291,7 +291,7 @@ namespace } Log *log = DefaultEnv::GetLog(); - log->Info( FileMsg, "[0x%x@%s] Successfully recovered page #%d.", + log->Info( FileMsg, "[%p@%s] Successfully recovered page #%llu.", this, pgReadHandler->stateHandler->pFileUrl->GetObfuscatedURL().c_str(), pgnb ); DeleteArgs( 0, response, hostList ); @@ -800,7 +800,7 @@ namespace XrdCl if( !self->pFileUrl->IsValid() ) { - log->Error( FileMsg, "[0x%x@%s] Trying to open invalid url: %s", + log->Error( FileMsg, "[%p@%s] Trying to open invalid url: %s", self.get(), self->pFileUrl->GetPath().c_str(), url.c_str() ); self->pStatus = XRootDStatus( stError, errInvalidArgs ); self->pFileState = Closed; @@ -817,7 +817,7 @@ namespace XrdCl !self->pDoRecoverRead ) { self->pDoRecoverRead = false; - log->Debug( FileMsg, "[0x%x@%s] Read recovery procedures are disabled", + log->Debug( FileMsg, "[%p@%s] Read recovery procedures are disabled", self.get(), self->pFileUrl->GetObfuscatedURL().c_str() ); } @@ -826,14 +826,14 @@ namespace XrdCl !self->pDoRecoverWrite ) { self->pDoRecoverWrite = false; - log->Debug( FileMsg, "[0x%x@%s] Write recovery procedures are disabled", + log->Debug( FileMsg, "[%p@%s] Write recovery procedures are disabled", self.get(), self->pFileUrl->GetObfuscatedURL().c_str() ); } //-------------------------------------------------------------------------- // Open the file //-------------------------------------------------------------------------- - log->Debug( FileMsg, "[0x%x@%s] Sending an open command", self.get(), + log->Debug( FileMsg, "[%p@%s] Sending an open command", self.get(), self->pFileUrl->GetObfuscatedURL().c_str() ); self->pOpenMode = mode; @@ -898,8 +898,8 @@ namespace XrdCl self->pFileState = CloseInProgress; Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a close command for handle 0x%x to " - "%s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a close command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); //-------------------------------------------------------------------------- @@ -974,8 +974,8 @@ namespace XrdCl } Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a stat command for handle 0x%x to " - "%s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a stat command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); //-------------------------------------------------------------------------- @@ -1021,8 +1021,8 @@ namespace XrdCl return XRootDStatus( stError, errInvalidOp ); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a read command for handle 0x%x to " - "%s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a read command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); Message *msg; @@ -1076,7 +1076,7 @@ namespace XrdCl if( !issupported ) { - DefaultEnv::GetLog()->Debug( FileMsg, "[0x%x@%s] PgRead not supported; substituting with Read.", + DefaultEnv::GetLog()->Debug( FileMsg, "[%p@%s] PgRead not supported; substituting with Read.", self.get(), self->pFileUrl->GetObfuscatedURL().c_str() ); ResponseHandler *substitHandler = new PgReadSubstitutionHandler( self, handler ); auto st = Read( self, offset, size, buffer, substitHandler, timeout ); @@ -1124,8 +1124,8 @@ namespace XrdCl return XRootDStatus( stError, errInvalidOp ); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a pgread command for handle 0x%x to " - "%s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a pgread command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); Message *msg; @@ -1180,8 +1180,8 @@ namespace XrdCl return XRootDStatus( stError, errInvalidOp ); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a write command for handle 0x%x to " - "%s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a write command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); Message *msg; @@ -1226,9 +1226,9 @@ namespace XrdCl if( !XrdSys::KernelBuffer::IsPageAligned( buffer.GetBuffer() ) || self->pIsChannelEncrypted ) { Log *log = DefaultEnv::GetLog(); - log->Info( FileMsg, "[0x%x@%s] Buffer is not page aligned (4KB), cannot " - "convert it to kernel space buffer.", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), - *((uint32_t*)self->pFileHandle) ); + log->Info( FileMsg, "[%p@%s] Buffer for handle %#x is not page aligned (4KB), " + "cannot convert it to kernel space buffer.", self.get(), + self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle) ); void *buff = buffer.GetBuffer(); uint32_t size = buffer.GetSize(); @@ -1414,20 +1414,20 @@ namespace XrdCl r->Get( inf ); if( inf->NeedRetry() ) // so we failed in the end { - DefaultEnv::GetLog()->Warning( FileMsg, "[0x%x@%s] Failed retransmitting corrupted " + DefaultEnv::GetLog()->Warning( FileMsg, "[%p@%s] Failed retransmitting corrupted " "page: pgoff=%llu, pglen=%du, pgdigest=%du", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), pgoff, pglen, pgdigest ); pgwrt->SetStatus( new XRootDStatus( stError, errDataError, 0, "Failed to retransmit corrupted page" ) ); } else - DefaultEnv::GetLog()->Info( FileMsg, "[0x%x@%s] Succesfuly retransmitted corrupted " + DefaultEnv::GetLog()->Info( FileMsg, "[%p@%s] Succesfuly retransmitted corrupted " "page: pgoff=%llu, pglen=%du, pgdigest=%du", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), pgoff, pglen, pgdigest ); } ); auto st = PgWriteRetry( self, pgoff, pglen, pgbuf, pgdigest, h, timeout ); if( !st.IsOK() ) pgwrt->SetStatus( new XRootDStatus( st ) ); - DefaultEnv::GetLog()->Info( FileMsg, "[0x%x@%s] Retransmitting corrupted page: " + DefaultEnv::GetLog()->Info( FileMsg, "[%p@%s] Retransmitting corrupted page: " "pgoff=%llu, pglen=%du, pgdigest=%du", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), pgoff, pglen, pgdigest ); } @@ -1477,8 +1477,8 @@ namespace XrdCl return XRootDStatus( stError, errInvalidOp ); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a pgwrite command for handle 0x%x to " - "%s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a pgwrite command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); //-------------------------------------------------------------------------- @@ -1527,8 +1527,8 @@ namespace XrdCl return XRootDStatus( stError, errInvalidOp ); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a sync command for handle 0x%x to " - "%s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a sync command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); Message *msg; @@ -1566,8 +1566,8 @@ namespace XrdCl return XRootDStatus( stError, errInvalidOp ); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a truncate command for handle 0x%x to " - "%s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a truncate command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); Message *msg; @@ -1610,8 +1610,8 @@ namespace XrdCl return XRootDStatus( stError, errInvalidOp ); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a vector read command for handle " - "0x%x to %s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a vector read command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); //-------------------------------------------------------------------------- @@ -1686,8 +1686,8 @@ namespace XrdCl return XRootDStatus( stError, errInvalidOp ); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a vector write command for handle " - "0x%x to %s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a vector write command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); //-------------------------------------------------------------------------- @@ -1762,8 +1762,8 @@ namespace XrdCl return XRootDStatus( stError, errInvalidOp ); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a write command for handle 0x%x to " - "%s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a write command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); Message *msg; @@ -1818,8 +1818,8 @@ namespace XrdCl return XRootDStatus( stError, errInvalidOp ); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a read command for handle 0x%x to " - "%s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a read command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); Message *msg; @@ -1875,8 +1875,8 @@ namespace XrdCl return XRootDStatus( stError, errInvalidOp ); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a fcntl command for handle 0x%x to " - "%s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a fcntl command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); Message *msg; @@ -1916,8 +1916,8 @@ namespace XrdCl return XRootDStatus( stError, errInvalidOp ); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a visa command for handle 0x%x to " - "%s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a visa command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); Message *msg; @@ -1956,8 +1956,8 @@ namespace XrdCl return XRootDStatus( stError, errInvalidOp ); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a fattr set command for handle 0x%x to " - "%s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a fattr set command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); //-------------------------------------------------------------------------- @@ -1982,8 +1982,8 @@ namespace XrdCl return XRootDStatus( stError, errInvalidOp ); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a fattr get command for handle 0x%x to " - "%s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a fattr get command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); //-------------------------------------------------------------------------- @@ -2008,8 +2008,8 @@ namespace XrdCl return XRootDStatus( stError, errInvalidOp ); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a fattr del command for handle 0x%x to " - "%s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a fattr del command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); //-------------------------------------------------------------------------- @@ -2033,8 +2033,8 @@ namespace XrdCl return XRootDStatus( stError, errInvalidOp ); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a fattr list command for handle 0x%x to " - "%s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a fattr list command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); //-------------------------------------------------------------------------- @@ -2069,8 +2069,8 @@ namespace XrdCl return XRootDStatus( stError, errInvalidOp ); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a checkpoint command for " - "handle 0x%x to %s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a checkpoint command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); Message *msg; @@ -2120,8 +2120,8 @@ namespace XrdCl return XRootDStatus( stError, errInvalidOp ); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a write command for handle 0x%x to " - "%s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a write command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); Message *msg; @@ -2182,8 +2182,8 @@ namespace XrdCl return XRootDStatus( stError, errInvalidOp ); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a write command for handle 0x%x to " - "%s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a write command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); Message *msg; @@ -2358,7 +2358,7 @@ namespace XrdCl } } - log->Debug(FileMsg, "[0x%x@%s] Open has returned with status %s", + log->Debug(FileMsg, "[%p@%s] Open has returned with status %s", this, pFileUrl->GetObfuscatedURL().c_str(), status->ToStr().c_str() ); if( pDataServer && !pDataServer->IsLocalFile() ) @@ -2384,7 +2384,7 @@ namespace XrdCl pStatus = *status; if( !pStatus.IsOK() || !openInfo ) { - log->Debug(FileMsg, "[0x%x@%s] Error while opening at %s: %s", + log->Debug(FileMsg, "[%p@%s] Error while opening at %s: %s", this, pFileUrl->GetObfuscatedURL().c_str(), lastServer.c_str(), pStatus.ToStr().c_str() ); FailQueuedMessages( pStatus ); @@ -2419,7 +2419,7 @@ namespace XrdCl pStatInfo = new StatInfo( *openInfo->GetStatInfo() ); } - log->Debug( FileMsg, "[0x%x@%s] successfully opened at %s, handle: 0x%x, " + log->Debug( FileMsg, "[%p@%s] successfully opened at %s, handle: %#x, " "session id: %ld", this, pFileUrl->GetObfuscatedURL().c_str(), pDataServer->GetHostId().c_str(), *((uint32_t*)pFileHandle), pSessionId ); @@ -2455,13 +2455,12 @@ namespace XrdCl Log *log = DefaultEnv::GetLog(); XrdSysMutexHelper scopedLock( pMutex ); - log->Debug(FileMsg, "[0x%x@%s] Close returned from %s with: %s", this, + log->Debug(FileMsg, "[%p@%s] Close returned from %s with: %s", this, pFileUrl->GetObfuscatedURL().c_str(), pDataServer->GetHostId().c_str(), status->ToStr().c_str() ); - log->Dump(FileMsg, "[0x%x@%s] Items in the fly %d, queued for recovery %d", - this, pFileUrl->GetObfuscatedURL().c_str(), pInTheFly.size(), - pToBeRecovered.size() ); + log->Dump(FileMsg, "[%p@%s] Items in the fly %llu, queued for recovery %llu", + this, pFileUrl->GetObfuscatedURL().c_str(), pInTheFly.size(), pToBeRecovered.size() ); MonitorClose( status ); ResetMonitoringVars(); @@ -2505,7 +2504,7 @@ namespace XrdCl XrdSysMutexHelper scopedLock( self->pMutex ); self->pInTheFly.erase( message ); - log->Dump( FileMsg, "[0x%x@%s] File state error encountered. Message %s " + log->Dump( FileMsg, "[%p@%s] File state error encountered. Message %s " "returned with %s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), message->GetObfuscatedDescription().c_str(), status->ToStr().c_str() ); @@ -2540,7 +2539,7 @@ namespace XrdCl //-------------------------------------------------------------------------- if( !self->IsRecoverable( *status ) || sendParams.kbuff ) { - log->Error( FileMsg, "[0x%x@%s] Fatal file state error. Message %s " + log->Error( FileMsg, "[%p@%s] Fatal file state error. Message %s " "returned with %s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), message->GetObfuscatedDescription().c_str(), status->ToStr().c_str() ); @@ -2600,7 +2599,7 @@ namespace XrdCl Log *log = DefaultEnv::GetLog(); XrdSysMutexHelper scopedLock( self->pMutex ); - log->Dump( FileMsg, "[0x%x@%s] Got state response for message %s", + log->Dump( FileMsg, "[%p@%s] Got state response for message %s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), message->GetObfuscatedDescription().c_str() ); @@ -2719,7 +2718,7 @@ namespace XrdCl if( !pToBeRecovered.empty() ) { Log *log = DefaultEnv::GetLog(); - log->Dump( FileMsg, "[0x%x@%s] Got a timer event", this, + log->Dump( FileMsg, "[%p@%s] Got a timer event", this, pFileUrl->GetObfuscatedURL().c_str() ); RequestList::iterator it; JobManager *jobMan = DefaultEnv::GetPostMaster()->GetJobManager(); @@ -2752,7 +2751,7 @@ namespace XrdCl if( (IsReadOnly() && pDoRecoverRead) || (!IsReadOnly() && pDoRecoverWrite) ) { - log->Debug( FileMsg, "[0x%x@%s] Putting the file in recovery state in " + log->Debug( FileMsg, "[%p@%s] Putting the file in recovery state in " "process %d", this, pFileUrl->GetObfuscatedURL().c_str(), getpid() ); pFileState = Recovering; pInTheFly.clear(); @@ -2775,7 +2774,7 @@ namespace XrdCl self->pFileState = Recovering; Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Reopen file at next data server.", + log->Debug( FileMsg, "[%p@%s] Reopen file at next data server.", self.get(), self->pFileUrl->GetObfuscatedURL().c_str() ); // merge CGI @@ -2919,7 +2918,7 @@ namespace XrdCl self->pFileState = Recovering; Log *log = DefaultEnv::GetLog(); - log->Dump( FileMsg, "[0x%x@%s] Putting message %s in the recovery list", + log->Dump( FileMsg, "[%p@%s] Putting message %s in the recovery list", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), rd.request->GetObfuscatedDescription().c_str() ); @@ -2948,7 +2947,7 @@ namespace XrdCl return Status(); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Running the recovery procedure", self.get(), + log->Debug( FileMsg, "[%p@%s] Running the recovery procedure", self.get(), self->pFileUrl->GetObfuscatedURL().c_str() ); Status st; @@ -3008,7 +3007,7 @@ namespace XrdCl uint16_t timeout ) { Log *log = DefaultEnv::GetLog(); - log->Dump( FileMsg, "[0x%x@%s] Sending a recovery open command to %s", + log->Dump( FileMsg, "[%p@%s] Sending a recovery open command to %s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), url.GetObfuscatedURL().c_str() ); //-------------------------------------------------------------------------- @@ -3069,7 +3068,7 @@ namespace XrdCl void FileStateHandler::FailMessage( RequestData rd, XRootDStatus status ) { Log *log = DefaultEnv::GetLog(); - log->Dump( FileMsg, "[0x%x@%s] Failing message %s with %s", + log->Dump( FileMsg, "[%p@%s] Failing message %s with %s", this, pFileUrl->GetObfuscatedURL().c_str(), rd.request->GetObfuscatedDescription().c_str(), status.ToStr().c_str() ); @@ -3078,7 +3077,7 @@ namespace XrdCl if( !sh ) { Log *log = DefaultEnv::GetLog(); - log->Error( FileMsg, "[0x%x@%s] Internal error while recovering %s", + log->Error( FileMsg, "[%p@%s] Internal error while recovering %s", this, pFileUrl->GetObfuscatedURL().c_str(), rd.request->GetObfuscatedDescription().c_str() ); return; @@ -3189,7 +3188,7 @@ namespace XrdCl } Log *log = DefaultEnv::GetLog(); - log->Dump( FileMsg, "[0x%x@%s] Rewritten file handle for %s to 0x%x", + log->Dump( FileMsg, "[%p@%s] Rewritten file handle for %s to %#x", this, pFileUrl->GetObfuscatedURL().c_str(), msg->GetObfuscatedDescription().c_str(), *((uint32_t*)pFileHandle) ); XRootDTransport::SetDescription( msg ); @@ -3258,8 +3257,8 @@ namespace XrdCl return XRootDStatus( stError, errInvalidOp ); Log *log = DefaultEnv::GetLog(); - log->Debug( FileMsg, "[0x%x@%s] Sending a write command for handle 0x%x to " - "%s", self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), + log->Debug( FileMsg, "[%p@%s] Sending a write command for handle %#x to %s", + self.get(), self->pFileUrl->GetObfuscatedURL().c_str(), *((uint32_t*)self->pFileHandle), self->pDataServer->GetHostId().c_str() ); Message *msg; diff --git a/src/XrdCl/XrdClFileSystem.cc b/src/XrdCl/XrdClFileSystem.cc index 70ca88cd8bb..2e2e2bd691c 100644 --- a/src/XrdCl/XrdClFileSystem.cc +++ b/src/XrdCl/XrdClFileSystem.cc @@ -308,7 +308,7 @@ namespace //---------------------------------------------------------------------- if( !status->IsOK() ) { - log->Dump( FileSystemMsg, "[0x%x@DeepLocate(%s)] Got error " + log->Dump( FileSystemMsg, "[%p@DeepLocate(%s)] Got error " "response: %s", this, pPath.c_str(), status->ToStr().c_str() ); @@ -317,7 +317,7 @@ namespace //-------------------------------------------------------------------- if( pFirstTime ) { - log->Debug( FileSystemMsg, "[0x%x@DeepLocate(%s)] Failed to get " + log->Debug( FileSystemMsg, "[%p@DeepLocate(%s)] Failed to get " "the initial location list: %s", this, pPath.c_str(), status->ToStr().c_str() ); pHandler->HandleResponse( status, response ); @@ -334,7 +334,7 @@ namespace //-------------------------------------------------------------------- if( !pOutstanding ) { - log->Debug( FileSystemMsg, "[0x%x@DeepLocate(%s)] No outstanding " + log->Debug( FileSystemMsg, "[%p@DeepLocate(%s)] No outstanding " "requests, give out what we've got", this, pPath.c_str() ); scopedLock.UnLock(); @@ -352,7 +352,7 @@ namespace response->Get( info ); LocationInfo::Iterator it; - log->Dump( FileSystemMsg, "[0x%x@DeepLocate(%s)] Got %d locations", + log->Dump( FileSystemMsg, "[%p@DeepLocate(%s)] Got %d locations", this, pPath.c_str(), info->GetSize() ); for( it = info->Begin(); it != info->End(); ++it ) @@ -944,7 +944,7 @@ namespace XrdCl Log *log = DefaultEnv::GetLog(); XrdSysMutexHelper scopedLock( fs->pMutex ); - log->Dump( FileSystemMsg, "[0x%x@%s] Sending %s", fs.get(), + log->Dump( FileSystemMsg, "[%p@%s] Sending %s", fs.get(), fs->pUrl->GetHostId().c_str(), msg->GetObfuscatedDescription().c_str() ); AssignLastURLHandler *lastUrlHandler = new AssignLastURLHandler( fs, handler ); @@ -980,7 +980,7 @@ namespace XrdCl if( pLoadBalancerLookupDone ) return; - log->Dump( FileSystemMsg, "[0x%x@%s] Assigning %s as load balancer", this, + log->Dump( FileSystemMsg, "[%p@%s] Assigning %s as load balancer", this, pUrl->GetHostId().c_str(), url.GetHostId().c_str() ); pUrl.reset( new URL( url ) ); @@ -995,7 +995,7 @@ namespace XrdCl Log *log = DefaultEnv::GetLog(); XrdSysMutexHelper scopedLock( pMutex ); - log->Dump( FileSystemMsg, "[0x%x@%s] Assigning %s as last URL", this, + log->Dump( FileSystemMsg, "[%p@%s] Assigning %s as last URL", this, pUrl->GetHostId().c_str(), url.GetHostId().c_str() ); pLastUrl.reset( new URL( url ) ); diff --git a/src/XrdCl/XrdClInQueue.cc b/src/XrdCl/XrdClInQueue.cc index 6af97b29e79..46141dfce9d 100644 --- a/src/XrdCl/XrdClInQueue.cc +++ b/src/XrdCl/XrdClInQueue.cc @@ -86,14 +86,14 @@ namespace XrdCl handler = it->second.first; act = handler->Examine( msg ); exp = it->second.second; - log->Debug( ExDbgMsg, "[msg: 0x%x] Assigned MsgHandler: 0x%x.", + log->Debug( ExDbgMsg, "[msg: %p] Assigned MsgHandler: %p.", msg.get(), handler ); if( act & MsgHandler::RemoveHandler ) { pHandlers.erase( it ); - log->Debug( ExDbgMsg, "[handler: 0x%x] Removed MsgHandler: 0x%x from the in-queue.", + log->Debug( ExDbgMsg, "[handler: %p] Removed MsgHandler: %p from the in-queue.", handler, handler ); } } @@ -127,7 +127,7 @@ namespace XrdCl XrdSysMutexHelper scopedLock( pMutex ); pHandlers.erase(handlerSid); Log *log = DefaultEnv::GetLog(); - log->Debug( ExDbgMsg, "[handler: 0x%x] Removed MsgHandler: 0x%x from the in-queue.", + log->Debug( ExDbgMsg, "[handler: %p] Removed MsgHandler: %p from the in-queue.", handler, handler ); } diff --git a/src/XrdCl/XrdClJobManager.cc b/src/XrdCl/XrdClJobManager.cc index 63884912492..d3424606523 100644 --- a/src/XrdCl/XrdClJobManager.cc +++ b/src/XrdCl/XrdClJobManager.cc @@ -83,7 +83,7 @@ namespace XrdCl } } pRunning = true; - log->Debug( JobMgrMsg, "Job manager started, %d workers", pWorkers.size() ); + log->Debug( JobMgrMsg, "Job manager started, %llu workers", pWorkers.size() ); return true; } diff --git a/src/XrdCl/XrdClPollerBuiltIn.cc b/src/XrdCl/XrdClPollerBuiltIn.cc index 00c18968c35..a7c966554a1 100644 --- a/src/XrdCl/XrdClPollerBuiltIn.cc +++ b/src/XrdCl/XrdClPollerBuiltIn.cc @@ -269,7 +269,7 @@ namespace XrdCl return false; } - log->Debug( PollerMsg, "Adding socket 0x%x to the poller", socket ); + log->Debug( PollerMsg, "Adding socket %p to the poller", socket ); //-------------------------------------------------------------------------- // Check if the socket is already registered diff --git a/src/XrdCl/XrdClSocket.cc b/src/XrdCl/XrdClSocket.cc index 44917b5e3a6..09fd3ab7e78 100644 --- a/src/XrdCl/XrdClSocket.cc +++ b/src/XrdCl/XrdClSocket.cc @@ -529,7 +529,7 @@ namespace XrdCl // We have written the message successfully //---------------------------------------------------------------------- Log *log = DefaultEnv::GetLog(); - log->Dump( AsyncSockMsg, "[%s] Wrote a message: %s (0x%x), %d bytes", + log->Dump( AsyncSockMsg, "[%s] Wrote a message: %s (%p), %d bytes", strmname.c_str(), msg.GetObfuscatedDescription().c_str(), &msg, msg.GetSize() ); return XRootDStatus(); diff --git a/src/XrdCl/XrdClStream.cc b/src/XrdCl/XrdClStream.cc index 1b602dbb5d5..97f260099fd 100644 --- a/src/XrdCl/XrdClStream.cc +++ b/src/XrdCl/XrdClStream.cc @@ -322,7 +322,7 @@ namespace XrdCl path.up = 0; } - log->Dump( PostMasterMsg, "[%s] Sending message %s (0x%x) through " + log->Dump( PostMasterMsg, "[%s] Sending message %s (%p) through " "substream %d expecting answer at %d", pStreamName.c_str(), msg->GetObfuscatedDescription().c_str(), msg, path.up, path.down ); @@ -506,7 +506,7 @@ namespace XrdCl if( !handler ) { ServerResponse *rsp = (ServerResponse*)msg->GetBuffer(); - log->Warning( PostMasterMsg, "[%s] Discarding received message: 0x%x " + log->Warning( PostMasterMsg, "[%s] Discarding received message: %p " "(status=%d, SID=[%d,%d]), no MsgHandler found.", pStreamName.c_str(), msg.get(), rsp->hdr.status, rsp->hdr.streamid[0], rsp->hdr.streamid[1] ); @@ -516,12 +516,12 @@ namespace XrdCl //-------------------------------------------------------------------------- // We have a handler, so we call the callback //-------------------------------------------------------------------------- - log->Dump( PostMasterMsg, "[%s] Handling received message: 0x%x.", + log->Dump( PostMasterMsg, "[%s] Handling received message: %p.", pStreamName.c_str(), msg.get() ); if( action & (MsgHandler::NoProcess|MsgHandler::Ignore) ) { - log->Dump( PostMasterMsg, "[%s] Ignoring the processing handler for: 0x%x.", + log->Dump( PostMasterMsg, "[%s] Ignoring the processing handler for: %s.", pStreamName.c_str(), msg->GetObfuscatedDescription().c_str() ); // if we are handling partial response we have to take down the timeout fence @@ -647,8 +647,8 @@ namespace XrdCl //------------------------------------------------------------------------ if( pSubStreams.size() > 1 ) { - log->Debug( PostMasterMsg, "[%s] Attempting to connect %d additional " - "streams.", pStreamName.c_str(), pSubStreams.size()-1 ); + log->Debug( PostMasterMsg, "[%s] Attempting to connect %llu additional streams.", + pStreamName.c_str(), pSubStreams.size()-1 ); for( size_t i = 1; i < pSubStreams.size(); ++i ) { pSubStreams[i]->socket->SetAddress( pSubStreams[0]->socket->GetAddress() ); @@ -745,8 +745,8 @@ namespace XrdCl // Check if we still have time to try and do something in the current window //-------------------------------------------------------------------------- time_t elapsed = now-pConnectionInitTime; - log->Error( PostMasterMsg, "[%s] elapsed = %d, pConnectionWindow = %d " - "seconds.", pStreamName.c_str(), elapsed, pConnectionWindow ); + log->Error( PostMasterMsg, "[%s] elapsed = %ld, pConnectionWindow = %d seconds.", + pStreamName.c_str(), elapsed, pConnectionWindow ); //------------------------------------------------------------------------ // If we have some IP addresses left we try them @@ -775,8 +775,8 @@ namespace XrdCl else if( elapsed < pConnectionWindow && pConnectionCount < pConnectionRetry && !status.IsFatal() ) { - log->Info( PostMasterMsg, "[%s] Attempting reconnection in %d " - "seconds.", pStreamName.c_str(), pConnectionWindow-elapsed ); + log->Info( PostMasterMsg, "[%s] Attempting reconnection in %ld seconds.", + pStreamName.c_str(), pConnectionWindow-elapsed ); Task *task = new ::StreamConnectorTask( *pUrl, pStreamName ); pTaskManager->RegisterTask( task, pConnectionInitTime+pConnectionWindow ); diff --git a/src/XrdCl/XrdClUtils.cc b/src/XrdCl/XrdClUtils.cc index c5b3b4f826b..315eec4fd57 100644 --- a/src/XrdCl/XrdClUtils.cc +++ b/src/XrdCl/XrdClUtils.cc @@ -246,7 +246,7 @@ namespace XrdCl addrStr += ", "; } addrStr.erase( addrStr.length()-2, 2 ); - log->Debug( type, "[%s] Found %d address(es): %s", + log->Debug( type, "[%s] Found %llu address(es): %s", hostId.c_str(), addresses.size(), addrStr.c_str() ); } diff --git a/src/XrdCl/XrdClXRootDMsgHandler.cc b/src/XrdCl/XrdClXRootDMsgHandler.cc index 371aca15e59..0e93e2cd5ef 100644 --- a/src/XrdCl/XrdClXRootDMsgHandler.cc +++ b/src/XrdCl/XrdClXRootDMsgHandler.cc @@ -123,7 +123,7 @@ namespace XrdCl { Log *log = DefaultEnv::GetLog(); log->Warning( ExDbgMsg, "[%s] MsgHandler is examining a response although " - "it already owns a response: 0x%x (message: %s ).", + "it already owns a response: %p (message: %s ).", pUrl.GetHostId().c_str(), this, pRequest->GetObfuscatedDescription().c_str() ); } @@ -795,7 +795,7 @@ namespace XrdCl if( resendTime < pExpiration ) { - log->Debug( ExDbgMsg, "[%s] Scheduling WaitTask for MsgHandler: 0x%x (message: %s ).", + log->Debug( ExDbgMsg, "[%s] Scheduling WaitTask for MsgHandler: %p (message: %s ).", pUrl.GetHostId().c_str(), this, pRequest->GetObfuscatedDescription().c_str() ); @@ -906,7 +906,7 @@ namespace XrdCl log->Dump( XRootDMsg, "[%s] Message %s has been successfully sent.", pUrl.GetHostId().c_str(), message->GetObfuscatedDescription().c_str() ); - log->Debug( ExDbgMsg, "[%s] Moving MsgHandler: 0x%x (message: %s ) from out-queue to in-queue.", + log->Debug( ExDbgMsg, "[%s] Moving MsgHandler: %p (message: %s ) from out-queue to in-queue.", pUrl.GetHostId().c_str(), this, pRequest->GetObfuscatedDescription().c_str() ); @@ -1121,7 +1121,7 @@ namespace XrdCl AnyObject *response = 0; Log *log = DefaultEnv::GetLog(); - log->Debug( ExDbgMsg, "[%s] Calling MsgHandler: 0x%x (message: %s ) " + log->Debug( ExDbgMsg, "[%s] Calling MsgHandler: %p (message: %s ) " "with status: %s.", pUrl.GetHostId().c_str(), this, pRequest->GetObfuscatedDescription().c_str(), @@ -1659,7 +1659,7 @@ namespace XrdCl //------------------------------------------------------------------------ case kXR_readv: { - log->Dump( XRootDMsg, "[%s] Parsing the response to 0x%x as " + log->Dump( XRootDMsg, "[%s] Parsing the response to %p as " "VectorReadInfo", pUrl.GetHostId().c_str(), pRequest->GetObfuscatedDescription().c_str() ); @@ -2132,7 +2132,7 @@ namespace XrdCl if( pUrl.IsMetalink() && pFollowMetalink ) { - log->Debug( ExDbgMsg, "[%s] Metaling redirection for MsgHandler: 0x%x (message: %s ).", + log->Debug( ExDbgMsg, "[%s] Metaling redirection for MsgHandler: %p (message: %s ).", pUrl.GetHostId().c_str(), this, pRequest->GetObfuscatedDescription().c_str() ); @@ -2145,7 +2145,7 @@ namespace XrdCl } else { - log->Debug( ExDbgMsg, "[%s] Retry at server MsgHandler: 0x%x (message: %s ).", + log->Debug( ExDbgMsg, "[%s] Retry at server MsgHandler: %p (message: %s ).", pUrl.GetHostId().c_str(), this, pRequest->GetObfuscatedDescription().c_str() ); return pPostMaster->Send( pUrl, pRequest, this, true, pExpiration ); @@ -2249,7 +2249,7 @@ namespace XrdCl else { Log *log = DefaultEnv::GetLog(); - log->Debug( ExDbgMsg, "[%s] Passing to the thread-pool MsgHandler: 0x%x (message: %s ).", + log->Debug( ExDbgMsg, "[%s] Passing to the thread-pool MsgHandler: %p (message: %s ).", pUrl.GetHostId().c_str(), this, pRequest->GetObfuscatedDescription().c_str() ); jobMgr->QueueJob( new HandleRspJob( this ), 0 ); @@ -2262,7 +2262,7 @@ namespace XrdCl void XRootDMsgHandler::HandleLocalRedirect( URL *url ) { Log *log = DefaultEnv::GetLog(); - log->Debug( ExDbgMsg, "[%s] Handling local redirect - MsgHandler: 0x%x (message: %s ).", + log->Debug( ExDbgMsg, "[%s] Handling local redirect - MsgHandler: %p (message: %s ).", pUrl.GetHostId().c_str(), this, pRequest->GetObfuscatedDescription().c_str() ); diff --git a/src/XrdCl/XrdClXRootDMsgHandler.hh b/src/XrdCl/XrdClXRootDMsgHandler.hh index 403e09c6663..ee137aebd93 100644 --- a/src/XrdCl/XrdClXRootDMsgHandler.hh +++ b/src/XrdCl/XrdClXRootDMsgHandler.hh @@ -183,7 +183,7 @@ namespace XrdCl pHasSessionId = true; Log *log = DefaultEnv::GetLog(); - log->Debug( ExDbgMsg, "[%s] MsgHandler created: 0x%x (message: %s ).", + log->Debug( ExDbgMsg, "[%s] MsgHandler created: %p (message: %s ).", pUrl.GetHostId().c_str(), this, pRequest->GetObfuscatedDescription().c_str() ); @@ -222,7 +222,7 @@ namespace XrdCl pEffectiveDataServerUrl = reinterpret_cast( 0xDEADBEEF ); Log *log = DefaultEnv::GetLog(); - log->Debug( ExDbgMsg, "[%s] Destroying MsgHandler: 0x%x.", + log->Debug( ExDbgMsg, "[%s] Destroying MsgHandler: %p.", pUrl.GetHostId().c_str(), this ); } diff --git a/src/XrdCl/XrdClXRootDTransport.cc b/src/XrdCl/XrdClXRootDTransport.cc index cf1445e7e62..b98e0695f13 100644 --- a/src/XrdCl/XrdClXRootDTransport.cc +++ b/src/XrdCl/XrdClXRootDTransport.cc @@ -333,7 +333,7 @@ namespace XrdCl uint32_t bodySize = *(uint32_t*)(message.GetBuffer(4)); Log *log = DefaultEnv::GetLog(); - log->Dump( XRootDTransportMsg, "[msg: 0x%x] Expecting %d bytes of message " + log->Dump( XRootDTransportMsg, "[msg: %p] Expecting %d bytes of message " "body", &message, bodySize ); return XRootDStatus( stOK, suDone ); @@ -418,14 +418,14 @@ namespace XrdCl XRootDStatus st = XRootDTransport::UnMarchalStatusMore( message ); if( !st.IsOK() && st.code == errDataError ) { - log->Error( XRootDTransportMsg, "[msg: 0x%x] %s", &message, + log->Error( XRootDTransportMsg, "[msg: %p] %s", &message, st.GetErrorMessage().c_str() ); return st; } if( !st.IsOK() ) { - log->Error( XRootDTransportMsg, "[msg: 0x%x] Failed to unmarshall status body.", + log->Error( XRootDTransportMsg, "[msg: %p] Failed to unmarshall status body.", &message ); return st; } @@ -764,7 +764,7 @@ namespace XrdCl //-------------------------------------------------------------------------- XrdSysMutexHelper scopedLock( info->mutex ); uint16_t allocatedSIDs = info->sidManager->GetNumberOfAllocatedSIDs(); - log->Dump( XRootDTransportMsg, "[%s] Stream inactive since %d seconds, " + log->Dump( XRootDTransportMsg, "[%s] Stream inactive since %ld seconds, " "TTL: %d, allocated SIDs: %d, open files: %d, bound file objects: %d", info->streamName.c_str(), inactiveTime, ttl, allocatedSIDs, info->openFiles, info->finstcnt.load( std::memory_order_relaxed ) ); @@ -799,7 +799,7 @@ namespace XrdCl const bool anySID = info->sidManager->IsAnySIDOldAs( now - streamTimeout ); - log->Dump( XRootDTransportMsg, "[%s] Stream inactive since %d seconds, " + log->Dump( XRootDTransportMsg, "[%s] Stream inactive since %ld seconds, " "stream timeout: %d, any SID: %d, wait barrier: %s", info->streamName.c_str(), inactiveTime, streamTimeout, anySID, Utils::TimeToString(info->waitBarrier).c_str() ); @@ -1584,7 +1584,7 @@ namespace XrdCl if( info->sidManager->IsTimedOut( rsp->hdr.streamid ) ) { - log->Error( XRootDTransportMsg, "Message 0x%x, stream [%d, %d] is a " + log->Error( XRootDTransportMsg, "Message %p, stream [%d, %d] is a " "response that we're no longer interested in (timed out)", &msg, rsp->hdr.streamid[0], rsp->hdr.streamid[1] ); //------------------------------------------------------------------------ @@ -1618,7 +1618,7 @@ namespace XrdCl { seconds = ntohl( rsp->body.waitresp.seconds ); - log->Dump( XRootDMsg, "[%s] Got kXR_waitresp response of %d seconds, " + log->Dump( XRootDMsg, "[%s] Got kXR_waitresp response of %u seconds, " "setting up wait barrier.", info->streamName.c_str(), seconds ); diff --git a/src/XrdCl/XrdClZipArchive.cc b/src/XrdCl/XrdClZipArchive.cc index fb303d23668..7dbdbc65a48 100644 --- a/src/XrdCl/XrdClZipArchive.cc +++ b/src/XrdCl/XrdClZipArchive.cc @@ -93,7 +93,7 @@ namespace XrdCl // if it is a compressed file use ZIP cache to read from the file if( cdfh->compressionMethod == Z_DEFLATED ) { - log->Dump( ZipMsg, "[0x%x] Reading compressed data.", &me ); + log->Dump( ZipMsg, "[%p] Reading compressed data.", &me ); // check if respective ZIP cache exists bool empty = me.zipcache.find( fn ) == me.zipcache.end(); // if the entry does not exist, it will be created using @@ -156,7 +156,7 @@ namespace XrdCl [relativeOffset, rdbuff, &cache, &me]( XRootDStatus &st, RSP &rsp ) { Log *log = DefaultEnv::GetLog(); - log->Dump( ZipMsg, "[0x%x] Read %d bytes of remote data at offset %d.", + log->Dump( ZipMsg, "[%p] Read %d bytes of remote data at offset %llu.", &me, rsp.GetLength(), rsp.GetOffset() ); cache.QueueRsp( st, relativeOffset, std::move( *rdbuff ) ); }; @@ -172,7 +172,7 @@ namespace XrdCl if( size ) { memcpy( usrbuff, me.buffer.get() + offset, size ); - log->Dump( ZipMsg, "[0x%x] Serving read from local cache.", &me ); + log->Dump( ZipMsg, "[%p] Serving read from local cache.", &me ); } if( usrHandler ) @@ -187,8 +187,8 @@ namespace XrdCl Pipeline p = XrdCl::RdWithRsp( me.archive, offset, size, usrbuff ) >> [=, &me]( XRootDStatus &st, RSP &r ) { - log->Dump( ZipMsg, "[0x%x] Read %d bytes of remote data at " - "offset %d.", &me, r.GetLength(), r.GetOffset() ); + log->Dump( ZipMsg, "[%p] Read %d bytes of remote data at " + "offset %llu.", &me, r.GetLength(), r.GetOffset() ); if( usrHandler ) { XRootDStatus *status = ZipArchive::make_status( st ); @@ -243,12 +243,12 @@ namespace XrdCl { archsize = info.GetSize(); openstage = NotParsed; - log->Debug( ZipMsg, "[0x%x] Opened (only) a ZIP archive (%s).", + log->Debug( ZipMsg, "[%p] Opened (only) a ZIP archive (%s).", this, url.c_str() ); } else { - log->Error( ZipMsg, "[0x%x] Failed to open-only a ZIP archive (%s): %s", + log->Error( ZipMsg, "[%p] Failed to open-only a ZIP archive (%s): %s", this, url.c_str(), st.ToString().c_str() ); } @@ -288,7 +288,7 @@ namespace XrdCl { cdexists = false; openstage = Done; - log->Dump( ZipMsg, "[0x%x] Opened a ZIP archive (file empty).", this ); + log->Dump( ZipMsg, "[%p] Opened a ZIP archive (file empty).", this ); Pipeline::Stop(); } // prepare the arguments for the subsequent read @@ -297,8 +297,8 @@ namespace XrdCl buffer.reset( new char[*rdsize] ); rdbuff = buffer.get(); openstage = HaveEocdBlk; - log->Dump( ZipMsg, "[0x%x] Opened a ZIP archive, reading " - "Central Directory at offset: %d.", this, *rdoff ); + log->Dump( ZipMsg, "[%p] Opened a ZIP archive, reading " + "Central Directory at offset: %llu.", this, *rdoff ); } // read the Central Directory (in several stages if necessary) | XrdCl::Read( archive, rdoff, rdsize, rdbuff ) >> @@ -324,7 +324,7 @@ namespace XrdCl } try{ eocd.reset( new EOCD( eocdBlock, chunk.length - uint32_t(eocdBlock - buff) ) ); - log->Dump( ZipMsg, "[0x%x] EOCD record parsed: %s", this, + log->Dump( ZipMsg, "[%p] EOCD record parsed: %s", this, eocd->ToString().c_str() ); if(eocd->cdOffset > archsize || eocd->cdOffset + eocd->cdSize > archsize) throw bad_data(); @@ -371,7 +371,7 @@ namespace XrdCl buffer.reset( new char[*rdsize] ); rdbuff = buffer.get(); openstage = HaveCdRecords; - log->Dump( ZipMsg, "[0x%x] Reading additional data at offset: %d.", + log->Dump( ZipMsg, "[%p] Reading additional data at offset: %llu.", this, *rdoff ); Pipeline::Repeat(); break; // the break is really not needed ... } @@ -379,7 +379,7 @@ namespace XrdCl case HaveZip64EocdlBlk: { std::unique_ptr eocdl( new ZIP64_EOCDL( buff ) ); - log->Dump( ZipMsg, "[0x%x] EOCDL record parsed: %s", + log->Dump( ZipMsg, "[%p] EOCDL record parsed: %s", this, eocdl->ToString().c_str() ); if( chunk.offset > eocdl->zip64EocdOffset ) @@ -390,7 +390,7 @@ namespace XrdCl buffer.reset( new char[*rdsize] ); rdbuff = buffer.get(); openstage = HaveZip64EocdBlk; - log->Dump( ZipMsg, "[0x%x] Reading additional data at offset: %d.", + log->Dump( ZipMsg, "[%p] Reading additional data at offset: %llu.", this, *rdoff ); Pipeline::Repeat(); } @@ -410,7 +410,7 @@ namespace XrdCl Pipeline::Stop( error ); } zip64eocd.reset( new ZIP64_EOCD( buff ) ); - log->Dump( ZipMsg, "[0x%x] ZIP64EOCD record parsed: %s", + log->Dump( ZipMsg, "[%p] ZIP64EOCD record parsed: %s", this, zip64eocd->ToString().c_str() ); // now we can read the CD records, adjust the read arguments @@ -422,7 +422,7 @@ namespace XrdCl buffer.reset( new char[*rdsize] ); rdbuff = buffer.get(); openstage = HaveCdRecords; - log->Dump( ZipMsg, "[0x%x] Reading additional data at offset: %d.", + log->Dump( ZipMsg, "[%p] Reading additional data at offset: %llu.", this, *rdoff ); Pipeline::Repeat(); break; // the break is really not needed ... } @@ -438,7 +438,7 @@ namespace XrdCl std::tie( cdvec, cdmap ) = CDFH::Parse( buff, zip64eocd->cdSize, zip64eocd->nbCdRec ); else std::tie( cdvec, cdmap ) = CDFH::Parse( buff, eocd->cdSize, eocd->nbCdRec ); - log->Dump( ZipMsg, "[0x%x] CD records parsed.", this ); + log->Dump( ZipMsg, "[%p] CD records parsed.", this ); uint64_t sumCompSize = 0; for (auto it = cdvec.begin(); it != cdvec.end(); it++) { @@ -470,10 +470,10 @@ namespace XrdCl | XrdCl::Final( [=]( const XRootDStatus &status ) { // finalize the pipeline by calling the user callback if( status.IsOK() ) - log->Debug( ZipMsg, "[0x%x] Opened a ZIP archive (%s): %s", + log->Debug( ZipMsg, "[%p] Opened a ZIP archive (%s): %s", this, url.c_str(), status.ToString().c_str() ); else - log->Error( ZipMsg, "[0x%x] Failed to open a ZIP archive (%s): %s", + log->Error( ZipMsg, "[%p] Failed to open a ZIP archive (%s): %s", this, url.c_str(), status.ToString().c_str() ); if( handler ) handler->HandleResponse( make_status( status ), nullptr ); @@ -504,11 +504,11 @@ namespace XrdCl { openfn = fn; lfh.reset( new LFH( fn, crc32, size, time( 0 ) ) ); - log->Dump( ZipMsg, "[0x%x] File %s opened for append.", + log->Dump( ZipMsg, "[%p] File %s opened for append.", this, fn.c_str() ); return XRootDStatus(); } - log->Dump( ZipMsg, "[0x%x] Open failed: %s not in the ZIP archive.", + log->Dump( ZipMsg, "[%p] Open failed: %s not in the ZIP archive.", this, fn.c_str() ); return XRootDStatus( stError, errNotFound ); } @@ -517,14 +517,14 @@ namespace XrdCl // a file with the same name if( flags & OpenFlags::New ) { - log->Dump( ZipMsg, "[0x%x] Open failed: file exists %s, cannot append.", + log->Dump( ZipMsg, "[%p] Open failed: file exists %s, cannot append.", this, fn.c_str() ); return XRootDStatus( stError, errInvalidOp, EEXIST, "The file already exists in the ZIP archive." ); } openfn = fn; - log->Dump( ZipMsg, "[0x%x] File %s opened for reading.", + log->Dump( ZipMsg, "[%p] File %s opened for reading.", this, fn.c_str() ); return XRootDStatus(); } @@ -650,10 +650,10 @@ namespace XrdCl | XrdCl::Final( [=]( const XRootDStatus &st ) mutable { if( st.IsOK() ) - log->Dump( ZipMsg, "[0x%x] Successfully closed ZIP archive " + log->Dump( ZipMsg, "[%p] Successfully closed ZIP archive " "(CD written).", this ); else - log->Error( ZipMsg, "[0x%x] Failed to close ZIP archive: %s", + log->Error( ZipMsg, "[%p] Failed to close ZIP archive: %s", this, st.ToString().c_str() ); wrtbufs.clear(); if( handler ) handler->HandleResponse( make_status( st ), nullptr ); @@ -672,13 +672,13 @@ namespace XrdCl if( st.IsOK() ) { Clear(); - log->Dump( ZipMsg, "[0x%x] Successfully closed " + log->Dump( ZipMsg, "[%p] Successfully closed " "ZIP archive.", this ); } else { openstage = Error; - log->Error( ZipMsg, "[0x%x] Failed to close ZIP archive:" + log->Error( ZipMsg, "[%p] Failed to close ZIP archive:" " %s", this, st.ToString().c_str() ); } if( handler ) @@ -774,7 +774,7 @@ namespace XrdCl lfh->Serialize( *lfhbuf ); iov[0].iov_base = lfhbuf->data(); iov[0].iov_len = lfhlen; - log->Dump( ZipMsg, "[0x%x] Will write LFH.", this ); + log->Dump( ZipMsg, "[%p] Will write LFH.", this ); } //------------------------------------------------------------------------- // If there is no LFH just make the first chunk empty. @@ -886,12 +886,12 @@ namespace XrdCl // check if the file already exists in the archive if( itr != cdmap.end() ) { - log->Dump( ZipMsg, "[0x%x] Open failed: file exists %s, cannot append.", + log->Dump( ZipMsg, "[%p] Open failed: file exists %s, cannot append.", this, fn.c_str() ); return XRootDStatus( stError, errInvalidOp ); } - log->Dump( ZipMsg, "[0x%x] Appending file: %s.", this, fn.c_str() ); + log->Dump( ZipMsg, "[%p] Appending file: %s.", this, fn.c_str() ); //------------------------------------------------------------------------- // Create Local File Header record //------------------------------------------------------------------------- From 8f1b9beefa19de817a9cbc098aca8fa249a8f5c1 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Mon, 25 Nov 2024 15:29:05 +0100 Subject: [PATCH 242/276] [XrdEc] Fix wrong type of arguments to formatting function --- src/XrdEc/XrdEcReader.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/XrdEc/XrdEcReader.cc b/src/XrdEc/XrdEcReader.cc index 9ac55b5387e..e8a6e9903a2 100644 --- a/src/XrdEc/XrdEcReader.cc +++ b/src/XrdEc/XrdEcReader.cc @@ -980,7 +980,7 @@ namespace XrdEc if(!st.IsOK()) { - log->Dump(XrdCl::XRootDMsg, "EC Vector Read of host %d failed entirely.", i); + log->Dump(XrdCl::XRootDMsg, "EC Vector Read of host %llu failed entirely.", i); MissingVectorRead(currentBlock, blkid, strpid, timeout); } else{ @@ -1002,7 +1002,7 @@ namespace XrdEc uint32_t cksum = objcfg.digest( 0, currentBlock->stripes[strpid].data(), currentBlock->stripes[strpid].size() ); if( orgcksum != cksum ) { - log->Dump(XrdCl::XRootDMsg, "EC Vector Read: Wrong checksum for block %d stripe %d.", blkid, strpid); + log->Dump(XrdCl::XRootDMsg, "EC Vector Read: Wrong checksum for block %llu stripe %llu.", blkid, strpid); MissingVectorRead(currentBlock, blkid, strpid, timeout); continue; } @@ -1010,7 +1010,7 @@ namespace XrdEc currentBlock->state[strpid] = block_t::Valid; bool recoverable = currentBlock->error_correction( currentBlock ); if(!recoverable) - log->Dump(XrdCl::XRootDMsg, "EC Vector Read: Couldn't recover block %d.", blkid); + log->Dump(XrdCl::XRootDMsg, "EC Vector Read: Couldn't recover block %llu.", blkid); } } } @@ -1045,12 +1045,12 @@ namespace XrdEc // put received data into given buffers if(blockMap.find(blkid) == blockMap.end() || blockMap[blkid] == nullptr){ - log->Dump(XrdCl::XRootDMsg, "EC Vector Read: Missing block %d.", blkid); + log->Dump(XrdCl::XRootDMsg, "EC Vector Read: Missing block %llu.", blkid); failed = true; break; } if(blockMap[blkid]->state[strpid] != block_t::Valid){ - log->Dump(XrdCl::XRootDMsg, "EC Vector Read: Invalid stripe in block %d stripe %d.", blkid, strpid); + log->Dump(XrdCl::XRootDMsg, "EC Vector Read: Invalid stripe in block %llu stripe %llu.", blkid, strpid); failed = true; break; } From 3e53e892327b19bfa0a7a6c17529ace40e77c3a8 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Mon, 25 Nov 2024 15:31:34 +0100 Subject: [PATCH 243/276] [XrdClHttp] Fix wrong type of arguments to formatting function --- src/XrdClHttp/XrdClHttpFilePlugIn.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/XrdClHttp/XrdClHttpFilePlugIn.cc b/src/XrdClHttp/XrdClHttpFilePlugIn.cc index c20326d182f..f28025a44e3 100644 --- a/src/XrdClHttp/XrdClHttpFilePlugIn.cc +++ b/src/XrdClHttp/XrdClHttpFilePlugIn.cc @@ -186,11 +186,11 @@ XRootDStatus HttpFilePlugIn::Close(ResponseHandler *handler, return XRootDStatus(stError, errInvalidOp); } - logger_->Debug(kLogXrdClHttp, "Closing davix fd: %ld", davix_fd_); + logger_->Debug(kLogXrdClHttp, "Closing davix fd: %p", davix_fd_); auto status = Posix::Close(*davix_client_, davix_fd_); if (status.IsError()) { - logger_->Error(kLogXrdClHttp, "Could not close davix fd: %ld, error: %s", + logger_->Error(kLogXrdClHttp, "Could not close davix fd: %p, error: %s", davix_fd_, status.ToStr().c_str()); return status; } @@ -272,7 +272,7 @@ XRootDStatus HttpFilePlugIn::Read(uint64_t offset, uint32_t size, void *buffer, curr_offset = offset + num_bytes_read; if (avoid_pread_) offset_locker.unlock(); - logger_->Debug(kLogXrdClHttp, "Read %d bytes, at offset %d, from URL: %s", + logger_->Debug(kLogXrdClHttp, "Read %d bytes, at offset %llu, from URL: %s", num_bytes_read, offset, url_.c_str()); auto status = new XRootDStatus(); @@ -369,7 +369,7 @@ XRootDStatus HttpFilePlugIn::Write(uint64_t offset, uint32_t size, else filesize += res.first; - logger_->Debug(kLogXrdClHttp, "Wrote %d bytes, at offset %d, to URL: %s", + logger_->Debug(kLogXrdClHttp, "Wrote %d bytes, at offset %llu, to URL: %s", res.first, offset, url_.c_str()); handler->HandleResponse(new XRootDStatus(), nullptr); From e0081b7f281a3a22619af3e505211abf1dc54646 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Mon, 25 Nov 2024 15:39:44 +0100 Subject: [PATCH 244/276] [XrdCl] Fix typo in arguments to formatting function: s% -> %s --- src/XrdCl/XrdClXCpSrc.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/XrdCl/XrdClXCpSrc.cc b/src/XrdCl/XrdClXCpSrc.cc index c19332c7422..41eaa8db0c4 100644 --- a/src/XrdCl/XrdClXCpSrc.cc +++ b/src/XrdCl/XrdClXCpSrc.cc @@ -490,7 +490,7 @@ void XCpSrc::Steal( XCpSrc *src ) // need to notify pCtx->NotifyIdleSrc(); - log->Debug( UtilityMsg, "s%: Stealing everything from %s", myHost.c_str(), srcHost.c_str() ); + log->Debug( UtilityMsg, "%s: Stealing everything from %s", myHost.c_str(), srcHost.c_str() ); return; } @@ -515,7 +515,7 @@ void XCpSrc::Steal( XCpSrc *src ) pBlkEnd = src->pBlkEnd; src->pBlkEnd -= steal; - log->Debug( UtilityMsg, "s%: Stealing fraction (%f) of block from %s", myHost.c_str(), fraction, srcHost.c_str() ); + log->Debug( UtilityMsg, "%s: Stealing fraction (%f) of block from %s", myHost.c_str(), fraction, srcHost.c_str() ); return; } @@ -530,7 +530,7 @@ void XCpSrc::Steal( XCpSrc *src ) src->pRecovered.erase( itr ); } - log->Debug( UtilityMsg, "s%: Stealing fraction (%f) of recovered chunks from %s", myHost.c_str(), fraction, srcHost.c_str() ); + log->Debug( UtilityMsg, "%s: Stealing fraction (%f) of recovered chunks from %s", myHost.c_str(), fraction, srcHost.c_str() ); return; } @@ -551,7 +551,7 @@ void XCpSrc::Steal( XCpSrc *src ) src->pOngoing.erase( itr ); } - log->Debug( UtilityMsg, "s%: Stealing fraction (%f) of ongoing chunks from %s", myHost.c_str(), fraction, srcHost.c_str() ); + log->Debug( UtilityMsg, "%s: Stealing fraction (%f) of ongoing chunks from %s", myHost.c_str(), fraction, srcHost.c_str() ); } } @@ -567,7 +567,7 @@ XRootDStatus XCpSrc::GetWork() Log *log = DefaultEnv::GetLog(); std::string myHost = URL( pUrl ).GetHostName(); - log->Debug( UtilityMsg, "s% got next block", myHost.c_str() ); + log->Debug( UtilityMsg, "%s got next block", myHost.c_str() ); return XRootDStatus(); } From 50aec7fe2063d8137f56b1626150209df45f7719 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Mon, 25 Nov 2024 16:02:31 +0100 Subject: [PATCH 245/276] [XrdEc] Fix comparison of narrow type with wide type in loop condition --- src/XrdEc/XrdEcRedundancyProvider.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/XrdEc/XrdEcRedundancyProvider.cc b/src/XrdEc/XrdEcRedundancyProvider.cc index c41232aee28..fe899751ac4 100644 --- a/src/XrdEc/XrdEcRedundancyProvider.cc +++ b/src/XrdEc/XrdEcRedundancyProvider.cc @@ -177,7 +177,9 @@ RedundancyProvider::CodingTable& RedundancyProvider::getCodingTable( const std:: /* Expand pattern */ int nerrs = 0, nsrcerrs = 0; unsigned char err_indx_list[objcfg.nbparity]; - for (std::uint8_t i = 0; i < pattern.size(); i++) { + /* Avoid narrowing cast warning, size is always < 256 */ + uint8_t n = static_cast(pattern.size() & 0xff); + for (uint8_t i = 0; i < n; i++) { if (pattern[i]) { err_indx_list[nerrs++] = i; if (i < objcfg.nbdata) { nsrcerrs++; } From 103e70fa994e5fd04652ccbd10460fcd94f62172 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Mon, 25 Nov 2024 16:37:17 +0100 Subject: [PATCH 246/276] [XrdXrootd] Fix pointer to readahead_list in do_ReadNone --- src/XrdXrootd/XrdXrootdXeq.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/XrdXrootd/XrdXrootdXeq.cc b/src/XrdXrootd/XrdXrootdXeq.cc index ff1a4f855ac..78292614ce5 100644 --- a/src/XrdXrootd/XrdXrootdXeq.cc +++ b/src/XrdXrootd/XrdXrootdXeq.cc @@ -2504,7 +2504,7 @@ int XrdXrootdProtocol::do_ReadNone(int &retc, int &pathID) XrdXrootdFHandle fh; int ralsz = Request.header.dlen; struct read_args *rargs=(struct read_args *)(argp->buff); - struct readahead_list *ralsp = (readahead_list *)(rargs+sizeof(read_args)); + struct readahead_list *ralsp = (readahead_list *)(rargs+1); // Return the pathid // From 65a6eee60f5d79c6bd626f931f139e68980b61d8 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Mon, 25 Nov 2024 17:21:36 +0100 Subject: [PATCH 247/276] [XrdCl] Add missing argument to logging message --- src/XrdCl/XrdClCopy.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/XrdCl/XrdClCopy.cc b/src/XrdCl/XrdClCopy.cc index 53117e68a63..47b21570daf 100644 --- a/src/XrdCl/XrdClCopy.cc +++ b/src/XrdCl/XrdClCopy.cc @@ -838,7 +838,7 @@ int main( int argc, char **argv ) } log->Dump( AppMsg, "Processing source entry: %s, type %s, target file: %s, logLevel = %d", sourcePathObf.c_str(), FileType2String( sourceFile->Protocol ), - destPathObf.c_str() ); + destPathObf.c_str(), log->GetLevel() ); //-------------------------------------------------------------------------- // Set up the job From c3696ef585df3b8c2ff9663255565b9a92abb0cf Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 09:09:35 +0100 Subject: [PATCH 248/276] [XrdXrootd] Fix time-of-check vs time-of-use race condition Between the call to stat and the open, the file size may change. First open the file, then use fstat with the file descriptor to ensure consistency. --- src/XrdXrootd/XrdXrootdPrepare.cc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/XrdXrootd/XrdXrootdPrepare.cc b/src/XrdXrootd/XrdXrootdPrepare.cc index 94700936228..581c72d676c 100644 --- a/src/XrdXrootd/XrdXrootdPrepare.cc +++ b/src/XrdXrootd/XrdXrootdPrepare.cc @@ -258,14 +258,19 @@ int XrdXrootdPrepare::Open(const char *reqid, int &fsz) strcpy(path, (const char *)LogDir); strcpy(path+LogDirLen, reqid); +// Open the file and return the file descriptor +// + if ((fd = open((const char *)path, O_RDONLY)) < 0) return -errno; + // Get the file size // - if (stat((const char *)path, &buf)) return -errno; + if (fstat(fd, &buf) != 0) { + close(fd); + return -errno; + } + fsz = buf.st_size; -// Open the file and return the file descriptor -// - if ((fd = open((const char *)path, O_RDONLY)) < 0) return -errno; return fd; } From 63c4cb3a1a3c360bc8b77fc090f648e011dfe991 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 09:39:31 +0100 Subject: [PATCH 249/276] [XrdHttp] Fix time-of-check vs time-of-use race condition First open then fstat, instead of stat then fopen, to guarantee that the information from stat and open are consistent with each other. Afterwards, convert file descriptor to FILE* with fdopen. Ensure also that the file is closed should an error occur. --- src/XrdHttp/XrdHttpProtocol.cc | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/XrdHttp/XrdHttpProtocol.cc b/src/XrdHttp/XrdHttpProtocol.cc index b6ebd12fd67..ba8af7435f2 100644 --- a/src/XrdHttp/XrdHttpProtocol.cc +++ b/src/XrdHttp/XrdHttpProtocol.cc @@ -2235,20 +2235,31 @@ int XrdHttpProtocol::xsecretkey(XrdOucStream & Config) { if (val[0] == '/') { struct stat st; inFile = true; - if ( stat(val, &st) ) { - eDest.Emsg("Config", errno, "stat shared secret key file", val); + int fd = open(val, O_RDONLY); + + if ( fd == -1 ) { + eDest.Emsg("Config", errno, "open shared secret key file", val); + return 1; + } + + if ( fstat(fd, &st) != 0 ) { + eDest.Emsg("Config", errno, "fstat shared secret key file", val); + close(fd); return 1; } if ( st.st_mode & S_IWOTH & S_IWGRP & S_IROTH) { - eDest.Emsg("Config", "For your own security, the shared secret key file cannot be world readable or group writable'", val, "'"); + eDest.Emsg("Config", + "For your own security, the shared secret key file cannot be world readable or group writable '", val, "'"); + close(fd); return 1; } - FILE *fp = fopen(val,"r"); + FILE *fp = fdopen(fd, "r"); - if( fp == NULL ) { - eDest.Emsg("Config", errno, "open shared secret key file", val); + if ( fp == nullptr ) { + eDest.Emsg("Config", errno, "fdopen shared secret key file", val); + close(fd); return 1; } From 31a41eb89ad14ab4ebc577f8c99fe39b39fe59b8 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 09:48:09 +0100 Subject: [PATCH 250/276] [XrdCrypto] Reorder includes and fix stat vs open race condition Make sure that the information is consistent by first opening and subsequently using the same file descriptor for all operations. Since we needed to add an include as well, make sure to include our own headers first, then system headers. --- src/XrdCrypto/XrdCryptosslX509.cc | 43 ++++++++++++++++++++-------- src/XrdCrypto/XrdCryptosslX509Crl.cc | 28 +++++++++++------- 2 files changed, 48 insertions(+), 23 deletions(-) diff --git a/src/XrdCrypto/XrdCryptosslX509.cc b/src/XrdCrypto/XrdCryptosslX509.cc index 01fbdb25389..428302d70af 100644 --- a/src/XrdCrypto/XrdCryptosslX509.cc +++ b/src/XrdCrypto/XrdCryptosslX509.cc @@ -31,12 +31,6 @@ /* OpenSSL implementation of XrdCryptoX509 */ /* */ /* ************************************************************************** */ -#include -#include -#include -#include -#include - #include "XrdCrypto/XrdCryptosslRSA.hh" #include "XrdCrypto/XrdCryptosslX509.hh" #include "XrdCrypto/XrdCryptosslAux.hh" @@ -44,6 +38,14 @@ #include +#include +#include + +#include +#include +#include +#include + #define BIO_PRINT(b,c) \ BUF_MEM *bptr; \ BIO_get_mem_ptr(b, &bptr); \ @@ -90,19 +92,28 @@ XrdCryptosslX509::XrdCryptosslX509(const char *cf, const char *kf) } // Make sure file exists; struct stat st; - if (stat(cf, &st) != 0) { + int fd = open(cf, O_RDONLY); + + if (fd == -1) { if (errno == ENOENT) { DEBUG("file "< -#include -#include -#include -#include - #include "XrdCrypto/XrdCryptosslRSA.hh" #include "XrdCrypto/XrdCryptosslX509Crl.hh" #include "XrdCrypto/XrdCryptosslAux.hh" @@ -45,6 +39,14 @@ #include #include +#include +#include + +#include +#include +#include +#include + #if OPENSSL_VERSION_NUMBER < 0x10100000L #define X509_REVOKED_get0_revocationDate(x) (x)->revocationDate #define X509_REVOKED_get0_serialNumber(x) (x)->serialNumber @@ -164,21 +166,25 @@ int XrdCryptosslX509Crl::Init(const char *cf) DEBUG("file name undefined"); return -1; } + // Make sure file exists; - struct stat st; - if (stat(cf, &st) != 0) { + int fd = open(cf, O_RDONLY); + + if (fd == -1) { if (errno == ENOENT) { DEBUG("file "< Date: Tue, 26 Nov 2024 10:57:04 +0100 Subject: [PATCH 251/276] [XrdApps] First open then stat file to get consistent information --- src/XrdApps/Xrdadler32.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/XrdApps/Xrdadler32.cc b/src/XrdApps/Xrdadler32.cc index ab333656ec1..3c512551982 100644 --- a/src/XrdApps/Xrdadler32.cc +++ b/src/XrdApps/Xrdadler32.cc @@ -186,14 +186,14 @@ int main(int argc, char *argv[]) } if (argc == 1 || path[0] == '\0') { /* this is a local file */ - if (argc > 1) + if (argc > 1) { strcpy(path, argv[1]); - rc = stat(path, &stbuf); - if (rc != 0 || ! S_ISREG(stbuf.st_mode) || - (fd = open(path,O_RDONLY)) < 0) + if ((fd = open(path, O_RDONLY)) < 0 || fstat(fd, &stbuf) != 0 || !S_ISREG(stbuf.st_mode)) { - printf("Error_accessing %s\n", path); + if (fd != -1) + close(fd); + printf("Error opening %s: %s\n", path, strerror(errno)); return 1; } else /* see if the adler32 is saved in attribute already */ From 02f508369bde45a1db4827487422c214c74cb039 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 11:35:58 +0100 Subject: [PATCH 252/276] [XrdOuc] Fix potential null pointer dereference in copyCGI --- src/XrdOuc/XrdOucTPC.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/XrdOuc/XrdOucTPC.cc b/src/XrdOuc/XrdOucTPC.cc index 4920e281c3f..b3a46ce751f 100644 --- a/src/XrdOuc/XrdOucTPC.cc +++ b/src/XrdOuc/XrdOucTPC.cc @@ -237,13 +237,15 @@ int XrdOucTPC::copyCGI(const char *cgi, char *Buff, int Blen) int xlen; bool eqs; + if (!cgi) {*Buff = 0; return 0;} + // Skip over initial ampersands // while(*cgi == '&' && *cgi) cgi++; // Check if there is anything here // - if (!cgi || *cgi == 0) {*Buff = 0; return 0;} + if (!*cgi) {*Buff = 0; return 0;} Blen--; // Copy all keys except system oriented ones. From c3b458a5d17d38a01861a5fa556ca2146d2a30cb Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 11:45:05 +0100 Subject: [PATCH 253/276] [Secsss] Fix mismatched new/free in XrdSecsssID::~XrdSecsssID() The defaultID is created with defaultID = new XrdSecsssEnt(...); so it cannot be deallocated with free(defaultID). Since the destructor is private, we cannot call delete defaultID either. We must call the specific function for this, which is defaultID->Delete(), which in turn will call delete instead of free to deallocate the memory. --- src/XrdSecsss/XrdSecsssID.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/XrdSecsss/XrdSecsssID.cc b/src/XrdSecsss/XrdSecsssID.cc index 7616211bf57..b5f2e6708af 100644 --- a/src/XrdSecsss/XrdSecsssID.cc +++ b/src/XrdSecsss/XrdSecsssID.cc @@ -132,7 +132,7 @@ XrdSecsssID::XrdSecsssID(authType aType, const XrdSecEntity *idP, /* Private: D e s t r u c t o r */ /******************************************************************************/ -XrdSecsssID::~XrdSecsssID() {if (defaultID) free(defaultID);} +XrdSecsssID::~XrdSecsssID() {if (defaultID) defaultID->Delete();} /******************************************************************************/ /* Private: F i n d */ From 58bb106c93e3b3618c01fb337d488b1c6ca85c85 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 11:59:35 +0100 Subject: [PATCH 254/276] [Secztn] Fix integer overflow check in DecodeUrl() Using pointers in expressions that may cause overflow leads to undefined behavior. See also https://lwn.net/Articles/278137/ --- src/XrdSecztn/XrdSecztn.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/XrdSecztn/XrdSecztn.cc b/src/XrdSecztn/XrdSecztn.cc index 036baed646e..2054d6abbb3 100644 --- a/src/XrdSecztn/XrdSecztn.cc +++ b/src/XrdSecztn/XrdSecztn.cc @@ -28,6 +28,7 @@ #include #include #include +#include #ifndef __FreeBSD__ #include @@ -81,7 +82,10 @@ namespace int DecodeUrl(const char *decode, size_t num_decode, char *out, size_t &num_out) { // No integer overflows please. - if ((decode + num_decode) < decode || (out + num_out) < out) + if (num_decode > std::numeric_limits::max() - (size_t)decode) + return 1; + + if (num_out > std::numeric_limits::max() - (size_t)out) return 1; if (num_out < DecodeBytesNeeded(num_decode)) From 9611aa219ab6a5b694413e417ca1e0b3aedb0f03 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 12:28:58 +0100 Subject: [PATCH 255/276] [XrdPss] Fix potential null pointer dereference in copyCGI --- src/XrdPss/XrdPssUrlInfo.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/XrdPss/XrdPssUrlInfo.cc b/src/XrdPss/XrdPssUrlInfo.cc index f7055a60988..390eef45c17 100644 --- a/src/XrdPss/XrdPssUrlInfo.cc +++ b/src/XrdPss/XrdPssUrlInfo.cc @@ -54,7 +54,7 @@ int copyCGI(const char *cgi, char *Buff, int Blen) { int n; -//std::cerr <<"PSS cgi IN: '" < Date: Tue, 26 Nov 2024 12:30:53 +0100 Subject: [PATCH 256/276] [XrdXrootd] Remove redundant null pointer check The variable jkey is already checked to be non-null a few lines above. --- src/XrdXrootd/XrdXrootdJob.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/XrdXrootd/XrdXrootdJob.cc b/src/XrdXrootd/XrdXrootdJob.cc index 467323de5ae..f208366c545 100644 --- a/src/XrdXrootd/XrdXrootdJob.cc +++ b/src/XrdXrootd/XrdXrootdJob.cc @@ -612,7 +612,7 @@ int XrdXrootdJob::Schedule(const char *jkey, // First find if this is a duplicate or create a new one // myMutex.Lock(); - if (!(Opts & JOB_Unique) && jkey && (jp = JobTable.Find(jkey))) + if (!(Opts & JOB_Unique) && (jp = JobTable.Find(jkey))) {if (jp->Status == XrdXrootdJob2Do::Job_Done) {rc = sendResult(resp, args[0], jp); myMutex.UnLock(); From cc422893da86e29bf750aca2bdb23371bb5b80d7 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 12:34:29 +0100 Subject: [PATCH 257/276] [XrdNet] Remove redundant null pointer check for ifList In line 707, there is already an if(!ifList || !(*ifList)) which makes the check in line 732 redundant. --- src/XrdNet/XrdNetIF.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/XrdNet/XrdNetIF.cc b/src/XrdNet/XrdNetIF.cc index 5e06b79da12..6d774c781a4 100644 --- a/src/XrdNet/XrdNetIF.cc +++ b/src/XrdNet/XrdNetIF.cc @@ -729,7 +729,6 @@ bool XrdNetIF::SetIF(XrdNetAddrInfo *src, const char *ifList, int port, // Process the iflist (up to four interfaces) // - if (ifList && *ifList) do {while (*ifBeg && *ifBeg == ' ') ifBeg++; if ( !(*ifBeg)) break; if (!(ifEnd = index(ifBeg, ' '))) {ifAdr = ifBeg; ifBeg = "";} From 00b0ba56cffb40477add0e75798877b56d323d52 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 12:42:07 +0100 Subject: [PATCH 258/276] [XrdEc] Fix CodeQL warning about overflow before conversion to size_t The multiplication of int * int might be bigger than an int can hold, but still fits in a size_t. Convert before multiplying to ensure no overflow. --- src/XrdEc/XrdEcRedundancyProvider.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/XrdEc/XrdEcRedundancyProvider.cc b/src/XrdEc/XrdEcRedundancyProvider.cc index fe899751ac4..ad1e6467c96 100644 --- a/src/XrdEc/XrdEcRedundancyProvider.cc +++ b/src/XrdEc/XrdEcRedundancyProvider.cc @@ -87,10 +87,11 @@ static int gf_gen_decode_matrix( unsigned char* invert_matrix, * backup, * b, s; int incr = 0; - std::vector memory((size_t) (m * k * 3)); + size_t mk = (size_t)m * (size_t)k; + std::vector memory(3 * mk); b = &memory[0]; - backup = &memory[m * k]; - invert_matrix = &memory[2 * m * k]; + backup = &memory[mk]; + invert_matrix = &memory[2 * mk]; // Construct matrix b by removing error rows for (i = 0, r = 0; i < k; i++, r++) { @@ -109,7 +110,7 @@ static int gf_gen_decode_matrix( return -1; } incr++; - memcpy(b, backup, (size_t) (m * k)); + memcpy(b, backup, mk); for (i = nsrcerrs; i < nerrs - nsrcerrs; i++) { if (src_err_list[i] == (decode_index[k - 1] + incr)) { // skip the erased parity line From e1349ec011eaef19d3b2e6037fb027f6a6d2b6f9 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 14:02:52 +0100 Subject: [PATCH 259/276] [XrdOss] Fix CodeQL warning about int overflow in multiplication The multiplication is numq * xfrovhd, which if carried with ints can overflow before being converted to unsigned long long. Convert one of the operands to unsigned long long to avoid this. --- src/XrdOss/XrdOssStage.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/XrdOss/XrdOssStage.cc b/src/XrdOss/XrdOssStage.cc index 4fad9b7dde3..539030489a7 100644 --- a/src/XrdOss/XrdOssStage.cc +++ b/src/XrdOss/XrdOssStage.cc @@ -393,8 +393,9 @@ int XrdOssSys::CalcTime() int XrdOssSys::CalcTime(XrdOssStage_Req *req) // StageMutex lock held! { + unsigned long long numq = 1; unsigned long long tbytes = req->size + stgbytes/2; - int xfrtime, numq = 1; + int xfrtime; time_t now; XrdOssStage_Req *rqp = req; From 7b68df22bf8604d632063c3d8384dbf7ecca08db Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 14:14:29 +0100 Subject: [PATCH 260/276] [XrdDig] Fix potentially overflowing call to snprintf From the man page for snprintf: The functions snprintf() and vsnprintf() do not write more than size bytes (including the terminating null byte ('\0')). If the output was truncated due to this limit, then the return value is the number of characters (excluding the terminating null byte) *which would have been written to the final string if enough space had been available*. Thus, a return value of size or more means that the output was truncated. We therefore need to check if n > buffer size, and act accordingly. --- src/XrdDig/XrdDigAuth.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/XrdDig/XrdDigAuth.cc b/src/XrdDig/XrdDigAuth.cc index 6460ffe3160..bfbc8d32b11 100644 --- a/src/XrdDig/XrdDigAuth.cc +++ b/src/XrdDig/XrdDigAuth.cc @@ -278,8 +278,10 @@ bool XrdDigAuth::Parse(XrdOucStream &aFile, int lNum) return Failure(lNum, "Invalid entity type -", var); if (*(var+1) != '=' || !*(var+2)) return Failure(lNum, "Badly formed entity value in", var); - n = snprintf(bP, bLeft, "%s", var+2) + 1; - if ((bLeft -= n) <= 0) break; + n = snprintf(bP, bLeft, "%s", var+2); + if (n < 0 || n >= bLeft) break; + ++n; + bLeft -= n; if ((var = index(bP, '\\'))) Squash(var); aEnt.eP->eChk[eCode-eVec] = bP; bP += n; aOK = true; From ec9a8ba8dc861e1cfeae36b62a3903c098b2f825 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 15:12:24 +0100 Subject: [PATCH 261/276] [CI] Use node18 in GitHub Actions for CentOS 7 --- .github/workflows/CI.yml | 2 +- .github/workflows/RPM.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 921d37929a9..388ac49437e 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -95,7 +95,7 @@ jobs: env: ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true - ACTIONS_RUNNER_FORCE_ACTIONS_NODE_VERSION: node16 + ACTIONS_RUNNER_FORCE_ACTIONS_NODE_VERSION: node18 CMAKE_ARGS: "-DCMAKE_INSTALL_PREFIX=/usr;-DCMAKE_INSTALL_RPATH='$ORIGIN/../$LIB'" steps: diff --git a/.github/workflows/RPM.yml b/.github/workflows/RPM.yml index c0c06436ad4..2c0c635cf1a 100644 --- a/.github/workflows/RPM.yml +++ b/.github/workflows/RPM.yml @@ -26,7 +26,7 @@ jobs: env: ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true - ACTIONS_RUNNER_FORCE_ACTIONS_NODE_VERSION: node16 + ACTIONS_RUNNER_FORCE_ACTIONS_NODE_VERSION: node18 steps: - name: Use CentOS 7 Vault Repository URLs From 104c93a8c3423dd977b45dd1dbdce8f9f9d7dec5 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 15:48:01 +0100 Subject: [PATCH 262/276] [CI] Do not upload RPM artifacts for CentOS 7 The action to upload-artifact@v3 no longer works due to its reliance on node16, which has just hit EOL and been removed. Since upload-artifact@v4 also doesn't work on CentOS 7, the only option to keep builds running is to not upload results. https://github.blog/changelog/2024-04-16-deprecation-notice-v3-of-the-artifact-actions/ --- .github/workflows/RPM.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.github/workflows/RPM.yml b/.github/workflows/RPM.yml index 2c0c635cf1a..21a9611793f 100644 --- a/.github/workflows/RPM.yml +++ b/.github/workflows/RPM.yml @@ -69,16 +69,6 @@ jobs: - name: Run post-install tests run: tests/post-install.sh - - name: Move RPMs to Artifact Directory - run: mkdir RPMS && mv $(rpm -E '%{_rpmdir}')/*/*.rpm RPMS/ - - - name: Upload Artifacts - uses: actions/upload-artifact@v3 - with: - name: centos7 - path: RPMS - retention-days: 14 - alma8: name: Alma Linux 8 runs-on: ubuntu-latest From 0faeaa947332867970cc7b05f91b975905deeaa1 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 15:52:16 +0100 Subject: [PATCH 263/276] [CI] Move to actions/checkout@v4 on Alpine Linux --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 388ac49437e..42858178ad1 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -72,7 +72,7 @@ jobs: zlib-dev - name: Clone repository - uses: actions/checkout@v1 + uses: actions/checkout@v4 - name: Setup GitHub runner user within container run: adduser -D --uid 1001 runner && chown -R runner:runner ${GITHUB_WORKSPACE} From b44b8275e448b8af67bfa16d75327762aa9056f2 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 16:05:11 +0100 Subject: [PATCH 264/276] [CI] Add AlmaLinux 10 beta build to GitHub Actions --- .github/workflows/CI.yml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 42858178ad1..f13989e1280 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -205,6 +205,42 @@ jobs: tests/post-install.sh tests/check-headers.sh + alma10beta: + name: Alma 10 (beta) + runs-on: ubuntu-latest + container: almalinux:10-kitten + + env: + CMAKE_ARGS: "-DCMAKE_INSTALL_PREFIX=/usr;-DCMAKE_INSTALL_RPATH='$ORIGIN/../$LIB'" + + steps: + - name: Install dependencies + run: | + dnf install -y dnf-plugins-core epel-release git rpmdevtools sudo + dnf config-manager --set-enabled crb + + - name: Clone repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup GitHub runner user within container + run: adduser --uid 1001 runner && chown -R runner:runner ${GITHUB_WORKSPACE} + + - name: Install XRootD build dependencies + run: dnf builddep -y xrootd.spec + + - name: Build and Test with CTest + run: sudo -E -u runner ctest -VV -S test.cmake + + - name: Install with CMake + run: cmake --install build + + - name: Run post-install tests + run: | + tests/post-install.sh + tests/check-headers.sh + fedora: name: Fedora runs-on: ubuntu-latest From 0b357545915b6c27e72958c4ae6a8a6c71673c53 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 16:06:37 +0100 Subject: [PATCH 265/276] [CI] Add AlmaLinux 10 beta RPM build to GitHub Actions --- .github/workflows/RPM.yml | 45 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/.github/workflows/RPM.yml b/.github/workflows/RPM.yml index 21a9611793f..c1aaffccc86 100644 --- a/.github/workflows/RPM.yml +++ b/.github/workflows/RPM.yml @@ -159,6 +159,51 @@ jobs: path: RPMS retention-days: 14 + alma10beta: + name: Alma Linux 10 (beta) + runs-on: ubuntu-latest + container: almalinux:9 + + steps: + - name: Install git + run: yum install -y git + + - name: Clone repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install RPM development tools + run: | + dnf install -y epel-release rpmdevtools dnf-plugins-core + dnf config-manager --set-enabled crb + + - name: Install XRootD build dependencies + run: dnf builddep -y xrootd.spec + + - name: Build RPMs + run: | + rpmdev-setuptree + git config --global --add safe.directory "$GITHUB_WORKSPACE" + git archive --prefix xrootd/ -o $(rpm -E '%{_sourcedir}')/xrootd.tar.gz HEAD + rpmbuild -bb --with git xrootd.spec + + - name: Install RPMs + run: dnf install -y $(rpm -E '%{_rpmdir}')/*/*.rpm + + - name: Run post-install tests + run: tests/post-install.sh + + - name: Move RPMs to Artifact Directory + run: mkdir RPMS && mv $(rpm -E '%{_rpmdir}')/ RPMS$(rpm -E '%{dist}' | tr . /) + + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: alma10 + path: RPMS + retention-days: 14 + fedora: name: Fedora 40 runs-on: ubuntu-latest From eb8a2a39c6237c2e371205535766a85241d60dbd Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 16:08:38 +0100 Subject: [PATCH 266/276] [CI] Add AlmaLinux 10 beta build to GitLab CI --- .gitlab-ci.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 882c10a0388..0a6791024b1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -104,6 +104,13 @@ AlmaLinux 9: - dnf config-manager --set-enabled crb <<: *rpm_build_dnf +AlmaLinux 10 (beta): + image: almalinux:10-kitten + before_script: + - dnf install -y epel-release + - dnf config-manager --set-enabled crb + <<: *rpm_build_dnf + Fedora 39: image: fedora:39 <<: *rpm_build_dnf From 1b787db55ced65b05681f73d7f719e21bd653204 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 16:09:28 +0100 Subject: [PATCH 267/276] [CI] Replace Fedora 39 with Fedora 41 in GitLab CI --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0a6791024b1..bc7d0188635 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -111,10 +111,10 @@ AlmaLinux 10 (beta): - dnf config-manager --set-enabled crb <<: *rpm_build_dnf -Fedora 39: - image: fedora:39 - <<: *rpm_build_dnf - Fedora 40: image: fedora:40 <<: *rpm_build_dnf + +Fedora 41: + image: fedora:41 + <<: *rpm_build_dnf From 58fb995c3fa67b5d5d151a81130097738dfcbaff Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 16:21:17 +0100 Subject: [PATCH 268/276] [CI] Move to Fedora 41 in RPM build on GitHub Actions --- .github/workflows/RPM.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/RPM.yml b/.github/workflows/RPM.yml index c1aaffccc86..d3626b71d2e 100644 --- a/.github/workflows/RPM.yml +++ b/.github/workflows/RPM.yml @@ -205,9 +205,9 @@ jobs: retention-days: 14 fedora: - name: Fedora 40 + name: Fedora 41 runs-on: ubuntu-latest - container: fedora:40 + container: fedora:41 steps: - name: Install git From 2d9b214341ef802906dcee8343c3e10c4c6aa99c Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 17:11:26 +0100 Subject: [PATCH 269/276] [CI] Use actions/checkout@v1 on CentOS 7 build --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index f13989e1280..66b3a84a8be 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -118,7 +118,7 @@ jobs: yum install -y epel-rpm-macros git rpmdevtools sudo yum-utils - name: Clone repository - uses: actions/checkout@v3 + uses: actions/checkout@v1 with: fetch-depth: 0 From 53105879750a8e81a0ac2df56249db426bea87b2 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Tue, 26 Nov 2024 17:20:55 +0100 Subject: [PATCH 270/276] [CI] Add fetch-depth checkout option for Alpine Linux When moving to actions/checkout@v4, v1 was kept on Alpine Linux by mistake. The previous commit moved CentOS 7 properly to v1, and this commit fixes the problem in the Alpine Linux build. Fixes: 90968349e8c980fce4d52adcef3efbfe67c7429d --- .github/workflows/CI.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 66b3a84a8be..720c2976dd9 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -73,6 +73,8 @@ jobs: - name: Clone repository uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Setup GitHub runner user within container run: adduser -D --uid 1001 runner && chown -R runner:runner ${GITHUB_WORKSPACE} From 9f74352a5377b869988908e7cb522db97efb699a Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Fri, 29 Nov 2024 10:08:18 +0100 Subject: [PATCH 271/276] Revert "[Secsss] Fix upcast array used in pointer arithmetics" This reverts commit 50209b3a5ed361f0e2c8541632cea1e217035b39. The fix is incorrect, as struct XrdSecsssRR_Data actually derives from XrdSecsssRR_DataHdr, which introduces some members in between such that prData.Data is not actually at the beginning of the struct, but at some non-zero offset (i.e. different address than &prData). --- src/XrdSecsss/XrdSecProtocolsss.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/XrdSecsss/XrdSecProtocolsss.cc b/src/XrdSecsss/XrdSecProtocolsss.cc index 5fc13566d5f..28466cab72e 100644 --- a/src/XrdSecsss/XrdSecProtocolsss.cc +++ b/src/XrdSecsss/XrdSecProtocolsss.cc @@ -590,7 +590,7 @@ int XrdSecProtocolsss::getCred(XrdOucErrInfo *einfo, // Extract out the loginid. This messy code is for backwards compatibility. // - bP = prData.Data; eodP = prData.Data + dLen; + bP = prData.Data; eodP = dLen + (char *)&prData; while(bP < eodP) {idType = *bP++; if (!XrdOucPup::Unpack(&bP, eodP, &idP, idSz) || !idP || *idP == 0) From d5dcde1b499048a65270f4906456cdd741588e83 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Wed, 27 Nov 2024 16:04:11 +0100 Subject: [PATCH 272/276] [RPM] Update spec file for XRootD 5.7.2 --- xrootd.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xrootd.spec b/xrootd.spec index 2b592d4e2b3..0bf36edb9c6 100644 --- a/xrootd.spec +++ b/xrootd.spec @@ -25,7 +25,7 @@ License: LGPL-3.0-or-later AND BSD-2-Clause AND BSD-3-Clause AND curl AND MIT AN URL: https://xrootd.slac.stanford.edu %if !%{with git} -Version: 5.7.1 +Version: 5.7.2 Source0: https://xrootd.web.cern.ch/download/v%{version}/%{name}-%{version}.tar.gz %else %define git_version %(tar xzf %{_sourcedir}/%{name}.tar.gz -O xrootd/VERSION) @@ -948,6 +948,9 @@ fi %changelog +* Wed Nov 27 2024 Guilherme Amadio - 1:5.7.2-1 +- XRootD 5.7.2 + * Mon Sep 02 2024 Guilherme Amadio - 1:5.7.1-1 - XRootD 5.7.1 From 8a4ca755c57bd01b807a74a4aa3fc89b3f42116b Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Wed, 27 Nov 2024 16:04:31 +0100 Subject: [PATCH 273/276] XRootD 5.7.2 --- docs/ReleaseNotes.txt | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/docs/ReleaseNotes.txt b/docs/ReleaseNotes.txt index 5bb43c941d7..fb00f640979 100644 --- a/docs/ReleaseNotes.txt +++ b/docs/ReleaseNotes.txt @@ -5,6 +5,44 @@ XRootD Release Notes ============= +------------- +Version 5.7.2 +------------- + ++ **Performance Improvements** + **[XrdHttp]** Avoid calling `stat` on HTTP GET (#2299, #2300) + **[XrdPfc]** Fix behavior and improve performance of stat calls (#2349) + ++ **Major bug fixes** + **[XrdOuc]** Migrate away from `std::regex` to avoid stack overflow bug in C++ standard library + **[XrdHttp]** Client plugin returning XrdCl::errSocketTimeout triggers near-infinite loop in XrdHttp (#2357) + **[XrdHttp]** Invalid chunk framing for HTTP (#2351) + ++ **Minor bug fixes** + **[Misc]** Fix various issues reported by code scanning tool CodeQL + **[Python]** Do not build in parallel by default to avoid using too many threads (#2356) + **[Python]** Fix RPATH setting for Python bindings on macOS (#2350) + **[Python]** Make environment take precedence over default arguments of add_job (#1657) + **[XCache]** Add number of bytes prefetched and written to disk to the gstream record (#2366) + **[XCache]** Fix errors that happen under high load (#2360) + **[XCache]** Reduce verbosity of error messages (#2288, #2327, #2370) + **[XrdCl]** xrdfs ls on a directory on a server runs locate (#2120) + **[XrdHttpTPC]** Race condition during HTTP TPC request may cause file deletion (#2354) + **[XrdHttp]** Redact tokens from additional places to prevent them leaking into logs (#2343, #2371) + **[XrdSut]** Fix narrowing conversion on 32-bit systems (#2272) + **[XrdSys]** Protect against array index out of bounds (#2329) + ++ **Miscellaneous** + **[CI]** Update GitHub Actions and GitLab CI, add Alma 10 beta builds + **[CMake]** Support building with Nvidia HPC Toolkit compilers (#2361) + **[Doxygen]** Make documentation builds reproducible (#2337) + **[Tests]** Avoid test failures when $RANDOM returns a multiple of 1024 (#2344) + **[Tests]** Increase default timeouts in client/server tests + **[Tests]** More HTTP tests added to the test suite (#2375) + **[XrdCl]** Downgrade force disconnect error message to debug level (#2370) + **[XrdCl]** Handle kXR_attrCache attribute in protocol response + **[XrdCms]** Improve DFS error message to be less confusing (#2345) + ------------- Version 5.7.1 ------------- From 2c269c648168a488229539e921aa28e930df44c1 Mon Sep 17 00:00:00 2001 From: David Smith Date: Wed, 11 Dec 2024 19:40:45 +0100 Subject: [PATCH 274/276] [Seckrb5] Avoid null pointer dereference (#2385) --- src/XrdSeckrb5/XrdSecProtocolkrb5.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/XrdSeckrb5/XrdSecProtocolkrb5.cc b/src/XrdSeckrb5/XrdSecProtocolkrb5.cc index 6fd79400901..ddc9f7117fa 100644 --- a/src/XrdSeckrb5/XrdSecProtocolkrb5.cc +++ b/src/XrdSeckrb5/XrdSecProtocolkrb5.cc @@ -510,7 +510,9 @@ int XrdSecProtocolkrb5::Authenticate(XrdSecCredentials *cred, {char* cpName; int ec; isCP = true; - if ((ec = krb5_unparse_name(krb_context, + if (!Ticket || !Ticket->enc_part2) + cPrincipal = "[principal not available]"; + else if ((ec = krb5_unparse_name(krb_context, (krb5_const_principal)Ticket->enc_part2->client, (char **)&cpName))) {char mBuff[1024]; From aef92e0ef217938d02affbd7641d9395279829af Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Thu, 5 Dec 2024 16:26:45 +0100 Subject: [PATCH 275/276] [Tests] Use existing ENABLE_SERVER_TESTS option in tests/XrdCl This was an oversight when adding the option in commit below. Fixes: a5cfef7b28bbb31fb184077a455ad19aae77a3d1 --- tests/XrdCl/CMakeLists.txt | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/XrdCl/CMakeLists.txt b/tests/XrdCl/CMakeLists.txt index 6e06487c550..f448d85bb97 100644 --- a/tests/XrdCl/CMakeLists.txt +++ b/tests/XrdCl/CMakeLists.txt @@ -10,13 +10,7 @@ target_link_libraries(xrdcl-unit-tests gtest_discover_tests(xrdcl-unit-tests TEST_PREFIX XrdCl::) -if(XRDCL_ONLY) - return() -endif() - -execute_process(COMMAND id -u OUTPUT_VARIABLE UID OUTPUT_STRIP_TRAILING_WHITESPACE) - -if (UID EQUAL 0) +if(NOT ENABLE_SERVER_TESTS) return() endif() From c9aaf675bbf87adccc215cc0a29c5e25ee061f31 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Wed, 6 Nov 2024 12:55:58 +0100 Subject: [PATCH 276/276] [Tests] Fix check for running process to prevent setup failures If a test causes a server to crash, a pid file with the pid of the crashed server will be left behind. The teardown phase would then fail due to a non-zero return of the ps command when assigning the pid to $PID. This changes the test to skip killing a process that has already exited without failing. --- tests/XRootD/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/XRootD/test.sh b/tests/XRootD/test.sh index f8f6a81ad51..4bdb827df30 100755 --- a/tests/XRootD/test.sh +++ b/tests/XRootD/test.sh @@ -147,7 +147,7 @@ function teardown() { # Kill all processes that created pid files and are still running for PIDFILE in *.pid; do test -s "${PIDFILE}" || continue - PID="$(ps -o pid= "$(cat "${PIDFILE}")")" + PID="$(ps -o pid= "$(cat "${PIDFILE}")" || true)" if test -n "${PID}"; then kill -s TERM "${PID}" fi