XRootD
Loading...
Searching...
No Matches
XrdClCopyProcess.cc
Go to the documentation of this file.
1//------------------------------------------------------------------------------
2// Copyright (c) 2011-2014 by European Organization for Nuclear Research (CERN)
3// Author: Lukasz Janyst <ljanyst@cern.ch>
4//------------------------------------------------------------------------------
5// This file is part of the XRootD software suite.
6//
7// XRootD is free software: you can redistribute it and/or modify
8// it under the terms of the GNU Lesser General Public License as published by
9// the Free Software Foundation, either version 3 of the License, or
10// (at your option) any later version.
11//
12// XRootD is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15// GNU General Public License for more details.
16//
17// You should have received a copy of the GNU Lesser General Public License
18// along with XRootD. If not, see <http://www.gnu.org/licenses/>.
19//
20// In applying this licence, CERN does not waive the privileges and immunities
21// granted to it by virtue of its status as an Intergovernmental Organization
22// or submit itself to any jurisdiction.
23//------------------------------------------------------------------------------
24
28#include "XrdCl/XrdClCopyJob.hh"
31#include "XrdCl/XrdClLog.hh"
32#include "XrdCl/XrdClMonitor.hh"
35#include "XrdCl/XrdClUtils.hh"
38
39#include <sys/time.h>
40
41namespace
42{
43 class QueuedCopyJob: public XrdCl::Job
44 {
45 public:
46 QueuedCopyJob( XrdCl::CopyJob *job,
47 XrdCl::CopyProgressHandler *progress,
48 uint32_t currentJob,
49 uint32_t totalJobs,
50 XrdSysSemaphore *sem = 0 ):
51 pJob(job), pProgress(progress), pCurrentJob(currentJob),
52 pTotalJobs(totalJobs), pSem(sem),
53 pWrtRetryCnt( XrdCl::DefaultRetryWrtAtLBLimit ),
54 pRetryCnt( XrdCl::DefaultCpRetry ),
55 pRetryPolicy( XrdCl::DefaultCpRetryPolicy )
56 {
57 XrdCl::DefaultEnv::GetEnv()->GetInt( "RetryWrtAtLBLimit", pWrtRetryCnt );
58 XrdCl::DefaultEnv::GetEnv()->GetInt( "CpRetry", pRetryCnt );
59 XrdCl::DefaultEnv::GetEnv()->GetString( "CpRetryPolicy", pRetryPolicy );
60 }
61
62 //------------------------------------------------------------------------
64 //------------------------------------------------------------------------
65 virtual void Run( void * )
66 {
67 XrdCl::Monitor *mon = XrdCl::DefaultEnv::GetMonitor();
68 timeval bTOD;
69
70 //----------------------------------------------------------------------
71 // Report beginning of the copy
72 //----------------------------------------------------------------------
73 if( pProgress )
74 pProgress->BeginJob( pCurrentJob, pTotalJobs,
75 &pJob->GetSource(),
76 &pJob->GetTarget() );
77
78 if( mon )
79 {
80 XrdCl::Monitor::CopyBInfo i;
81 i.transfer.origin = &pJob->GetSource();
82 i.transfer.target = &pJob->GetTarget();
84 }
85
86 gettimeofday( &bTOD, 0 );
87
88 //----------------------------------------------------------------------
89 // Do the copy
90 //----------------------------------------------------------------------
91 XrdCl::XRootDStatus st;
92 while( true )
93 {
94 st = pJob->Run( pProgress );
95 //--------------------------------------------------------------------
96 // Retry due to write-recovery
97 //--------------------------------------------------------------------
98 if( !st.IsOK() && st.code == XrdCl::errRetry && pWrtRetryCnt > 0 )
99 {
100 std::string url;
101 pJob->GetResults()->Get( "LastURL", url );
102 XrdCl::URL lastURL( url );
103 XrdCl::URL::ParamsMap cgi = lastURL.GetParams();
104 auto itr = cgi.find( "tried" );
105 if( itr != cgi.end() )
106 {
107 std::string tried = itr->second;
108 if( tried[tried.size() - 1] != ',' ) tried += ',';
109 tried += lastURL.GetHostName();
110 cgi["tried"] = tried;
111 }
112 else
113 cgi["tried"] = lastURL.GetHostName();
114
115 std::string recoveryRedir;
116 pJob->GetResults()->Get( "WrtRecoveryRedir", recoveryRedir );
117 XrdCl::URL recRedirURL( recoveryRedir );
118
119 std::string target;
120 pJob->GetProperties()->Get( "target", target );
121 XrdCl::URL trgURL( target );
122 trgURL.SetHostName( recRedirURL.GetHostName() );
123 trgURL.SetPort( recRedirURL.GetPort() );
124 trgURL.SetProtocol( recRedirURL.GetProtocol() );
125 trgURL.SetParams( cgi );
126 pJob->GetProperties()->Set( "target", trgURL.GetURL() );
127 pJob->Init();
128
129 // we have a new job, let's try again
130 --pWrtRetryCnt;
131 continue;
132 }
133 //--------------------------------------------------------------------
134 // Copy job retry
135 //--------------------------------------------------------------------
136 if( !st.IsOK() && pRetryCnt > 0 &&
140 {
141 if( pRetryPolicy == "continue" )
142 {
143 pJob->GetProperties()->Set( "force", false );
144 pJob->GetProperties()->Set( "continue", true );
145 }
146 else
147 {
148 pJob->GetProperties()->Set( "force", true );
149 pJob->GetProperties()->Set( "continue", false );
150 }
151 --pRetryCnt;
152 continue;
153 }
154
155 // we only loop in case of retry error
156 break;
157 }
158
159 pJob->GetResults()->Set( "status", st );
160
161 //----------------------------------------------------------------------
162 // Report end of the copy
163 //----------------------------------------------------------------------
164 if( mon )
165 {
166 std::vector<std::string> sources;
167 pJob->GetResults()->Get( "sources", sources );
168 XrdCl::Monitor::CopyEInfo i;
169 i.transfer.origin = &pJob->GetSource();
170 i.transfer.target = &pJob->GetTarget();
171 i.sources = sources.size();
172 i.bTOD = bTOD;
173 gettimeofday( &i.eTOD, 0 );
174 i.status = &st;
176 }
177
178 if( pProgress )
179 pProgress->EndJob( pCurrentJob, pJob->GetResults() );
180
181 if( pSem )
182 pSem->Post();
183 }
184
185 private:
186 XrdCl::CopyJob *pJob;
187 XrdCl::CopyProgressHandler *pProgress;
188 uint32_t pCurrentJob;
189 uint32_t pTotalJobs;
190 XrdSysSemaphore *pSem;
191 int pWrtRetryCnt;
192 int pRetryCnt;
193 std::string pRetryPolicy;
194 };
195};
196
197namespace XrdCl
198{
200 {
201 std::vector<PropertyList> pJobProperties;
202 std::vector<PropertyList*> pJobResults;
203 std::vector<CopyJob*> pJobs;
204 };
205
206 //----------------------------------------------------------------------------
207 // Destructor
208 //----------------------------------------------------------------------------
210 {
211 }
212
213 //----------------------------------------------------------------------------
214 // Destructor
215 //----------------------------------------------------------------------------
217 {
218 CleanUpJobs();
219 delete pImpl;
220 }
221
222 //----------------------------------------------------------------------------
223 // Add job
224 //----------------------------------------------------------------------------
226 PropertyList *results )
227 {
228 Env *env = DefaultEnv::GetEnv();
229
230 //--------------------------------------------------------------------------
231 // Process a configuraion job
232 //--------------------------------------------------------------------------
233 if( properties.HasProperty( "jobType" ) &&
234 properties.Get<std::string>( "jobType" ) == "configuration" )
235 {
236 if( pImpl->pJobProperties.size() > 0 &&
237 pImpl->pJobProperties.rbegin()->HasProperty( "jobType" ) &&
238 pImpl->pJobProperties.rbegin()->Get<std::string>( "jobType" ) == "configuration" )
239 {
240 PropertyList &config = *pImpl->pJobProperties.rbegin();
241 PropertyList::PropertyMap::const_iterator it;
242 for( it = properties.begin(); it != properties.end(); ++it )
243 config.Set( it->first, it->second );
244 }
245 else
246 pImpl->pJobProperties.push_back( properties );
247 return XRootDStatus();
248 }
249
250 //--------------------------------------------------------------------------
251 // Validate properties
252 //--------------------------------------------------------------------------
253 if( !properties.HasProperty( "source" ) )
254 return XRootDStatus( stError, errInvalidArgs, 0, "source not specified" );
255
256 if( !properties.HasProperty( "target" ) )
257 return XRootDStatus( stError, errInvalidArgs, 0, "target not specified" );
258
259 pImpl->pJobProperties.push_back( properties );
260 PropertyList &p = pImpl->pJobProperties.back();
261
262 const char *bools[] = {"target", "force", "posc", "coerce", "makeDir",
263 "zipArchive", "xcp", "preserveXAttr", "rmOnBadCksum",
264 "continue", "zipAppend", "doServer", 0};
265 for( int i = 0; bools[i]; ++i )
266 if( !p.HasProperty( bools[i] ) )
267 p.Set( bools[i], false );
268
269 if( !p.HasProperty( "thirdParty" ) )
270 p.Set( "thirdParty", "none" );
271
272 if( !p.HasProperty( "thirdPartyMode" ) )
273 p.Set( "thirdPartyMode", "pull" );
274
275 if( !p.HasProperty( "checkSumMode" ) )
276 p.Set( "checkSumMode", "none" );
277 else
278 {
279 if( !p.HasProperty( "checkSumType" ) )
280 {
281 pImpl->pJobProperties.pop_back();
283 "checkSumType not specified" );
284 }
285 else
286 {
287 //----------------------------------------------------------------------
288 // Checksum type has to be case insensitive
289 //----------------------------------------------------------------------
290 std::string checkSumType;
291 p.Get( "checkSumType", checkSumType );
292 std::transform(checkSumType.begin(), checkSumType.end(),
293 checkSumType.begin(), ::tolower);
294 p.Set( "checkSumType", checkSumType );
295 }
296 }
297
298 if( !p.HasProperty( "parallelChunks" ) )
299 {
300 int val = DefaultCPParallelChunks;
301 env->GetInt( "CPParallelChunks", val );
302 p.Set( "parallelChunks", val );
303 }
304
305 if( !p.HasProperty( "chunkSize" ) )
306 {
307 int val = DefaultCPChunkSize;
308 env->GetInt( "CPChunkSize", val );
309 p.Set( "chunkSize", val );
310 }
311
312 if( !p.HasProperty( "xcpBlockSize" ) )
313 {
314 int val = DefaultXCpBlockSize;
315 env->GetInt( "XCpBlockSize", val );
316 p.Set( "xcpBlockSize", val );
317 }
318
319 if( !p.HasProperty( "initTimeout" ) )
320 {
321 int val = DefaultCPInitTimeout;
322 env->GetInt( "CPInitTimeout", val );
323 p.Set( "initTimeout", val );
324 }
325
326 if( !p.HasProperty( "tpcTimeout" ) )
327 {
328 int val = DefaultCPTPCTimeout;
329 env->GetInt( "CPTPCTimeout", val );
330 p.Set( "tpcTimeout", val );
331 }
332
333 if( !p.HasProperty( "cpTimeout" ) )
334 {
335 int val = DefaultCPTimeout;
336 env->GetInt( "CPTimeout", val );
337 p.Set( "cpTimeout", val );
338 }
339
340 if( !p.HasProperty( "dynamicSource" ) )
341 p.Set( "dynamicSource", false );
342
343 if( !p.HasProperty( "xrate" ) )
344 p.Set( "xrate", 0 );
345
346 if( !p.HasProperty( "xrateThreshold" ) || p.Get<long long>( "xrateThreshold" ) == 0 )
347 {
348 int val = DefaultXRateThreshold;
349 env->GetInt( "XRateThreshold", val );
350 p.Set( "xrateThreshold", val );
351 }
352
353 //--------------------------------------------------------------------------
354 // Insert the properties
355 //--------------------------------------------------------------------------
356 Log *log = DefaultEnv::GetLog();
357 Utils::LogPropertyList( log, UtilityMsg, "Adding job with properties: %s",
358 p );
359 pImpl->pJobResults.push_back( results );
360 return XRootDStatus();
361 }
362
363 //----------------------------------------------------------------------------
364 // Prepare the copy jobs
365 //----------------------------------------------------------------------------
367 {
368 Log *log = DefaultEnv::GetLog();
369 std::vector<PropertyList>::iterator it;
370
371 log->Debug( UtilityMsg, "CopyProcess: %zu jobs to prepare",
372 pImpl->pJobProperties.size() );
373
374 std::map<std::string, uint32_t> targetFlags;
375 int i = 0;
376 for( it = pImpl->pJobProperties.begin(); it != pImpl->pJobProperties.end(); ++it, ++i )
377 {
378 PropertyList &props = *it;
379
380 if( props.HasProperty( "jobType" ) &&
381 props.Get<std::string>( "jobType" ) == "configuration" )
382 continue;
383
384 PropertyList *res = pImpl->pJobResults[i];
385 std::string tmp;
386
387 props.Get( "source", tmp );
388 URL source = tmp;
389 if( !source.IsValid() )
390 {
391 log->Error( UtilityMsg, "Invalid copy source URL: %s", obfuscateAuth( tmp ).c_str() );
392 return XRootDStatus( stError, errInvalidArgs, 0, "invalid source" );
393 }
394
395 //--------------------------------------------------------------------------
396 // Create a virtual redirector if it is a Metalink file
397 //--------------------------------------------------------------------------
398 if( source.IsMetalink() )
399 {
401 XRootDStatus st = registry.RegisterAndWait( source );
402 if( !st.IsOK() ) return st;
403 }
404
405 // handle UNZIP CGI
406 const URL::ParamsMap &cgi = source.GetParams();
407 URL::ParamsMap::const_iterator itr = cgi.find( "xrdcl.unzip" );
408 if( itr != cgi.end() )
409 {
410 props.Set( "zipArchive", true );
411 props.Set( "zipSource", itr->second );
412 }
413
414 props.Get( "target", tmp );
415 URL target = tmp;
416 if( !target.IsValid() )
417 {
418 log->Error( UtilityMsg, "Invalid copy target URL: %s", obfuscateAuth( tmp ).c_str() );
419 return XRootDStatus( stError, errInvalidArgs, 0, "invalid target" );
420 }
421
422 if( target.GetProtocol() != "stdio" )
423 {
424 // handle directories
425 bool targetIsDir = false;
426 props.Get( "targetIsDir", targetIsDir );
427
428 if( targetIsDir )
429 {
430 std::string path = target.GetPath() + '/';
431 std::string fn;
432
433 bool isZip = false;
434 props.Get( "zipArchive", isZip );
435 if( isZip )
436 {
437 props.Get( "zipSource", fn );
438 }
439 else if( source.IsMetalink() )
440 {
442 VirtualRedirector *redirector = registry.Get( source );
443 fn = redirector->GetTargetName();
444 }
445 else
446 {
447 fn = source.GetPath();
448 }
449
450 size_t pos = fn.rfind( '/' );
451 if( pos != std::string::npos )
452 fn = fn.substr( pos + 1 );
453 path += fn;
454 target.SetPath( path );
455 props.Set( "target", target.GetURL() );
456 }
457 }
458
459 bool tpc = false;
460 props.Get( "thirdParty", tmp );
461 if( tmp != "none" )
462 tpc = true;
463
464 //------------------------------------------------------------------------
465 // Check if we have all we need
466 //------------------------------------------------------------------------
467 if( source.GetProtocol() != "stdio" && source.GetPath().empty() )
468 {
469 log->Debug( UtilityMsg, "CopyProcess (job #%d): no source specified.",
470 i );
471 CleanUpJobs();
473 res->Set( "status", st );
474 return st;
475 }
476
477 if( target.GetProtocol() != "stdio" && target.GetPath().empty() )
478 {
479 log->Debug( UtilityMsg, "CopyProcess (job #%d): no target specified.",
480 i );
481 CleanUpJobs();
483 res->Set( "status", st );
484 return st;
485 }
486
487 //------------------------------------------------------------------------
488 // Check what kind of job we should do
489 //------------------------------------------------------------------------
490 CopyJob *job = 0;
491
492 if( tpc == true )
493 {
494 MarkTPC( props );
495 job = new TPFallBackCopyJob( i+1, &props, res );
496 }
497 else
498 job = new ClassicCopyJob( i+1, &props, res );
499
500 pImpl->pJobs.push_back( job );
501 }
502 return XRootDStatus();
503 }
504
505 //----------------------------------------------------------------------------
506 // Run the copy jobs
507 //----------------------------------------------------------------------------
509 {
510 //--------------------------------------------------------------------------
511 // Get the configuration
512 //--------------------------------------------------------------------------
513 uint8_t parallelThreads = 1;
514 if( pImpl->pJobProperties.size() > 0 &&
515 pImpl->pJobProperties.rbegin()->HasProperty( "jobType" ) &&
516 pImpl->pJobProperties.rbegin()->Get<std::string>( "jobType" ) == "configuration" )
517 {
518 PropertyList &config = *pImpl->pJobProperties.rbegin();
519 if( config.HasProperty( "parallel" ) )
520 parallelThreads = (uint8_t)config.Get<int>( "parallel" );
521 }
522
523 //--------------------------------------------------------------------------
524 // Run the show
525 //--------------------------------------------------------------------------
526 std::vector<CopyJob *>::iterator it;
527 uint32_t currentJob = 1;
528 uint32_t totalJobs = pImpl->pJobs.size();
529
530 //--------------------------------------------------------------------------
531 // Single thread
532 //--------------------------------------------------------------------------
533 if( parallelThreads == 1 )
534 {
535 XRootDStatus err;
536
537 for( it = pImpl->pJobs.begin(); it != pImpl->pJobs.end(); ++it )
538 {
539 QueuedCopyJob j( *it, progress, currentJob, totalJobs );
540 j.Run(0);
541
542 XRootDStatus st = (*it)->GetResults()->Get<XRootDStatus>( "status" );
543 if( err.IsOK() && !st.IsOK() )
544 {
545 err = st;
546 }
547 ++currentJob;
548 }
549
550 if( !err.IsOK() ) return err;
551 }
552 //--------------------------------------------------------------------------
553 // Multiple threads
554 //--------------------------------------------------------------------------
555 else
556 {
557 uint32_t workers = std::min( (uint32_t)parallelThreads,
558 (uint32_t)pImpl->pJobs.size() );
559 JobManager jm( workers );
560 jm.Initialize();
561 if( !jm.Start() )
562 return XRootDStatus( stError, errOSError, 0,
563 "Unable to start job manager" );
564
565 XrdSysSemaphore *sem = new XrdSysSemaphore(0);
566 std::vector<QueuedCopyJob*> queued;
567 for( it = pImpl->pJobs.begin(); it != pImpl->pJobs.end(); ++it )
568 {
569 QueuedCopyJob *j = new QueuedCopyJob( *it, progress, currentJob,
570 totalJobs, sem );
571
572 queued.push_back( j );
573 jm.QueueJob(j, 0);
574 ++currentJob;
575 }
576
577 std::vector<QueuedCopyJob*>::iterator itQ;
578 for( itQ = queued.begin(); itQ != queued.end(); ++itQ )
579 sem->Wait();
580 delete sem;
581
582 if( !jm.Stop() )
583 return XRootDStatus( stError, errOSError, 0,
584 "Unable to stop job manager" );
585 jm.Finalize();
586 for( itQ = queued.begin(); itQ != queued.end(); ++itQ )
587 delete *itQ;
588
589 for( it = pImpl->pJobs.begin(); it != pImpl->pJobs.end(); ++it )
590 {
591 XRootDStatus st = (*it)->GetResults()->Get<XRootDStatus>( "status" );
592 if( !st.IsOK() ) return st;
593 }
594 };
595 return XRootDStatus();
596 }
597
598 void CopyProcess::CleanUpJobs()
599 {
600 std::vector<CopyJob*>::iterator itJ;
601 for( itJ = pImpl->pJobs.begin(); itJ != pImpl->pJobs.end(); ++itJ )
602 {
603 CopyJob *job = *itJ;
604 URL src = job->GetSource();
605 if( src.IsMetalink() )
606 {
608 registry.Release( src );
609 }
610 delete job;
611 }
612 pImpl->pJobs.clear();
613 }
614}
std::string obfuscateAuth(const std::string &input)
ClassicCopyJob(uint32_t jobId, PropertyList *jobProperties, PropertyList *jobResults)
const URL & GetSource() const
Get source.
virtual ~CopyProcess()
Destructor.
CopyProcess()
Constructor.
XRootDStatus Run(CopyProgressHandler *handler)
Run the copy jobs.
XRootDStatus AddJob(const PropertyList &properties, PropertyList *results)
Interface for copy progress notification.
static Monitor * GetMonitor()
Get the monitor object.
static Log * GetLog()
Get default log.
static Env * GetEnv()
Get default client environment.
bool GetString(const std::string &key, std::string &value)
Definition XrdClEnv.cc:31
bool GetInt(const std::string &key, int &value)
Definition XrdClEnv.cc:115
A synchronized queue.
bool Finalize()
Finalize the job manager, clear the queues.
bool Start()
Start the workers.
bool Initialize()
Initialize the job manager.
bool Stop()
Stop the workers.
void QueueJob(Job *job, void *arg=0)
Add a job to be run.
Interface for a job to be run by the job manager.
Handle diagnostics.
Definition XrdClLog.hh:101
void Error(uint64_t topic, const char *format,...)
Report an error.
Definition XrdClLog.cc:231
void Debug(uint64_t topic, const char *format,...)
Print a debug message.
Definition XrdClLog.cc:282
TransferInfo transfer
The transfer in question.
@ EvCopyBeg
CopyBInfo: Copy operation started.
@ EvCopyEnd
CopyEInfo: Copy operation ended.
virtual void Event(EventCode evCode, void *evData)=0
A key-value pair map storing both keys and values as strings.
void Set(const std::string &name, const Item &value)
PropertyMap::const_iterator end() const
Get the end iterator.
bool Get(const std::string &name, Item &item) const
bool HasProperty(const std::string &name) const
Check if we now about the given name.
PropertyMap::const_iterator begin() const
Get the begin iterator.
Singleton access to URL to virtual redirector mapping.
static RedirectorRegistry & Instance()
Returns reference to the single instance.
void Release(const URL &url)
Release the virtual redirector associated with the given URL.
XRootDStatus RegisterAndWait(const URL &url)
Creates a new virtual redirector and registers it (sync).
VirtualRedirector * Get(const URL &url) const
Get a virtual redirector associated with the given URL.
TPFallBackCopyJob(uint32_t jobId, PropertyList *jobProperties, PropertyList *jobResults)
Constructor.
URL representation.
Definition XrdClURL.hh:31
const std::string & GetPath() const
Get the path.
Definition XrdClURL.hh:217
bool IsMetalink() const
Is it a URL to a metalink.
Definition XrdClURL.cc:441
std::map< std::string, std::string > ParamsMap
Definition XrdClURL.hh:33
std::string GetURL() const
Get the URL.
Definition XrdClURL.hh:86
void SetPath(const std::string &path)
Set the path.
Definition XrdClURL.hh:225
const ParamsMap & GetParams() const
Get the URL params.
Definition XrdClURL.hh:244
const std::string & GetProtocol() const
Get the protocol.
Definition XrdClURL.hh:118
bool IsValid() const
Is the url valid.
Definition XrdClURL.cc:428
static void LogPropertyList(Log *log, uint64_t topic, const char *format, const PropertyList &list)
Log property list.
An interface for metadata redirectors.
virtual std::string GetTargetName() const =0
Gets the file name as specified in the metalink.
XRootDStatus(uint16_t st=0, uint16_t code=0, uint32_t errN=0, const std::string &message="")
Constructor.
XrdSysSemaphore(int semval=1, const char *=0)
const int DefaultCPInitTimeout
const int DefaultXRateThreshold
const uint16_t errOperationExpired
const int DefaultCPChunkSize
const uint16_t stError
An error occurred that could potentially be retried.
const int DefaultRetryWrtAtLBLimit
std::vector< PropertyList * > pJobResults
const int DefaultCPParallelChunks
const uint16_t errOSError
const int DefaultXCpBlockSize
const uint64_t UtilityMsg
const int DefaultCPTimeout
const uint16_t errInvalidArgs
std::vector< PropertyList > pJobProperties
const int DefaultCpRetry
const uint16_t errRetry
Try again for whatever reason.
const uint16_t errThresholdExceeded
const char *const DefaultCpRetryPolicy
const int DefaultCPTPCTimeout
std::vector< CopyJob * > pJobs
TransferInfo transfer
The transfer in question.
int sources
Number of sources used for the copy.
timeval bTOD
Copy start time.
const XRootDStatus * status
Status of the copy.
timeval eTOD
Copy end time.
const URL * target
URL of the target.
const URL * origin
URL of the origin.
uint16_t code
Error type, or additional hints on what to do.
bool IsOK() const
We're fine.
static bool IsSocketError(uint16_t code)