XRootD
XrdClCopy.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 
25 #include "XrdApps/XrdCpConfig.hh"
26 #include "XrdApps/XrdCpFile.hh"
27 #include "XrdCl/XrdClConstants.hh"
29 #include "XrdCl/XrdClDefaultEnv.hh"
30 #include "XrdCl/XrdClLog.hh"
31 #include "XrdCl/XrdClFileSystem.hh"
32 #include "XrdCl/XrdClUtils.hh"
33 #include "XrdCl/XrdClDlgEnv.hh"
34 #include "XrdCl/XrdClOptimizers.hh"
35 #include "XrdSys/XrdSysE2T.hh"
36 #include "XrdSys/XrdSysPthread.hh"
38 
39 #include <cstdio>
40 #include <iostream>
41 #include <iomanip>
42 #include <limits>
43 #include <string>
44 
45 using namespace std::string_literals;
46 
47 //------------------------------------------------------------------------------
48 // Progress notifier
49 //------------------------------------------------------------------------------
51 {
52  public:
53  //--------------------------------------------------------------------------
55  //--------------------------------------------------------------------------
56  ProgressDisplay(): pPrevious(0), pPrintProgressBar(true),
57  pPrintSourceCheckSum(false), pPrintTargetCheckSum(false),
58  pPrintAdditionalCheckSum(false)
59  {}
60 
61  //--------------------------------------------------------------------------
63  //--------------------------------------------------------------------------
64  virtual void BeginJob( uint32_t jobNum,
65  uint32_t jobTotal,
66  const XrdCl::URL *source,
67  const XrdCl::URL *destination )
68  {
69  XrdSysMutexHelper scopedLock( pMutex );
70  if( pPrintProgressBar )
71  {
72  if( jobTotal > 1 )
73  {
74  std::cerr << "Job: " << jobNum << "/" << jobTotal << std::endl;
75  std::cerr << "Source: " << source->GetURL() << std::endl;
76  std::cerr << "Target: " << destination->GetURL() << std::endl;
77  }
78  }
79  pPrevious = 0;
80 
81  JobData d;
82  d.started = time(0);
83  d.source = source;
84  d.target = destination;
85  pOngoingJobs[jobNum] = d;
86  }
87 
88  //--------------------------------------------------------------------------
90  //--------------------------------------------------------------------------
91  virtual void EndJob( uint32_t jobNum, const XrdCl::PropertyList *results )
92  {
93  XrdSysMutexHelper scopedLock( pMutex );
94 
95  std::map<uint32_t, JobData>::iterator it = pOngoingJobs.find( jobNum );
96  if( it == pOngoingJobs.end() )
97  return;
98 
99  JobData &d = it->second;
100 
101  // make sure the last available status was printed, which may not be
102  // the case when processing stdio since we throttle printing and don't
103  // know the total size
104  JobProgress( jobNum, d.bytesProcessed, d.bytesTotal );
105 
106  if( pPrintProgressBar )
107  {
108  if( pOngoingJobs.size() > 1 )
109  std::cerr << "\r" << std::string(70, ' ') << "\r";
110  else
111  std::cerr << std::endl;
112  }
113 
115  results->Get( "status", st );
116  if( !st.IsOK() )
117  {
118  pOngoingJobs.erase(it);
119  return;
120  }
121 
122  std::string checkSum;
123  uint64_t size;
124  results->Get( "size", size );
125  if( pPrintSourceCheckSum )
126  {
127  results->Get( "sourceCheckSum", checkSum );
128  PrintCheckSum( d.source, checkSum, size );
129  }
130 
131  if( pPrintTargetCheckSum )
132  {
133  results->Get( "targetCheckSum", checkSum );
134  PrintCheckSum( d.target, checkSum, size );
135  }
136 
137  if( pPrintAdditionalCheckSum )
138  {
139  std::vector<std::string> addcksums;
140  results->Get( "additionalCkeckSum", addcksums );
141  for( auto &cks : addcksums )
142  PrintCheckSum( d.source, cks, size );
143  }
144 
145  pOngoingJobs.erase(it);
146  }
147 
148  //--------------------------------------------------------------------------
150  //--------------------------------------------------------------------------
151  std::string GetProgressBar( time_t now )
152  {
153  JobData &d = pOngoingJobs.begin()->second;
154 
155  uint64_t speed = 0;
156  if( now-d.started )
157  speed = d.bytesProcessed/(now-d.started);
158  else
159  speed = d.bytesProcessed;
160 
161  std::string bar;
162  int prog = 0;
163  int proc = 0;
164 
165  if( d.bytesTotal )
166  {
167  prog = (int)((double)d.bytesProcessed/d.bytesTotal*50);
168  proc = (int)((double)d.bytesProcessed/d.bytesTotal*100);
169  }
170  else
171  {
172  prog = 50;
173  proc = 100;
174  }
175  bar.append( prog, '=' );
176  if( prog < 50 )
177  bar += ">";
178 
179  std::ostringstream o;
180  o << "[" << XrdCl::Utils::BytesToString(d.bytesProcessed) << "B/";
181  o << XrdCl::Utils::BytesToString(d.bytesTotal) << "B]";
182  o << "[" << std::setw(3) << std::right << proc << "%]";
183  o << "[" << std::setw(50) << std::left;
184  o << bar;
185  o << "]";
186  o << "[" << XrdCl::Utils::BytesToString(speed) << "B/s] ";
187  return o.str();
188  }
189 
190  //--------------------------------------------------------------------------
192  //--------------------------------------------------------------------------
193  std::string GetSummaryBar( time_t now )
194  {
195  std::map<uint32_t, JobData>::iterator it;
196  std::ostringstream o;
197 
198  for( it = pOngoingJobs.begin(); it != pOngoingJobs.end(); ++it )
199  {
200  JobData &d = it->second;
201  uint32_t jobNum = it->first;
202 
203  uint64_t speed = 0;
204  if( now-d.started )
205  speed = d.bytesProcessed/(now-d.started);
206 
207  int proc = 0;
208  if( d.bytesTotal )
209  proc = (int)((double)d.bytesProcessed/d.bytesTotal*100);
210  else
211  proc = 100;
212 
213  o << "[#" << jobNum << ": ";
214  o << proc << "% ";
215  o << XrdCl::Utils::BytesToString(speed) << "B/s] ";
216  }
217  o << " ";
218  return o.str();
219  }
220 
221  //--------------------------------------------------------------------------
223  //--------------------------------------------------------------------------
224  virtual void JobProgress( uint32_t jobNum,
225  uint64_t bytesProcessed,
226  uint64_t bytesTotal )
227  {
228  XrdSysMutexHelper scopedLock( pMutex );
229 
230  if( pPrintProgressBar )
231  {
232  time_t now = time(0);
233  if( (now - pPrevious < 1) && (bytesProcessed != bytesTotal) )
234  return;
235  pPrevious = now;
236 
237  std::map<uint32_t, JobData>::iterator it = pOngoingJobs.find( jobNum );
238  if( it == pOngoingJobs.end() )
239  return;
240 
241  JobData &d = it->second;
242 
243  d.bytesProcessed = bytesProcessed;
244  d.bytesTotal = bytesTotal;
245 
246  std::string progress;
247  if( pOngoingJobs.size() == 1 )
248  progress = GetProgressBar( now );
249  else
250  progress = GetSummaryBar( now );
251 
252  std::cerr << "\r" << progress << std::flush;
253  }
254  }
255 
256  //--------------------------------------------------------------------------
258  //--------------------------------------------------------------------------
259  void PrintCheckSum( const XrdCl::URL *url,
260  const std::string &checkSum,
261  uint64_t size )
262  {
263  if( checkSum.empty() )
264  return;
265  std::string::size_type i = checkSum.find( ':' );
266  std::cerr << checkSum.substr( 0, i+1 ) << " ";
267  std::cerr << checkSum.substr( i+1, checkSum.length()-i ) << " ";
268 
269  if( url->IsLocalFile() )
270  std::cerr << url->GetPath() << " ";
271  else
272  {
273  std::cerr << url->GetProtocol() << "://" << url->GetHostId();
274  std::cerr << url->GetPath() << " ";
275  }
276 
277  std::cerr << size;
278  std::cerr << std::endl;
279  }
280 
281  //--------------------------------------------------------------------------
282  // Printing flags
283  //--------------------------------------------------------------------------
284  void PrintProgressBar( bool print ) { pPrintProgressBar = print; }
285  void PrintSourceCheckSum( bool print ) { pPrintSourceCheckSum = print; }
286  void PrintTargetCheckSum( bool print ) { pPrintTargetCheckSum = print; }
287  void PrintAdditionalCheckSum( bool print ) { pPrintAdditionalCheckSum = print; }
288 
289  private:
290  struct JobData
291  {
292  JobData(): bytesProcessed(0), bytesTotal(0),
293  started(0), source(0), target(0) {}
294  uint64_t bytesProcessed;
295  uint64_t bytesTotal;
296  time_t started;
297  const XrdCl::URL *source;
298  const XrdCl::URL *target;
299  };
300 
301  time_t pPrevious;
302  bool pPrintProgressBar;
303  bool pPrintSourceCheckSum;
304  bool pPrintTargetCheckSum;
305  bool pPrintAdditionalCheckSum;
306  std::map<uint32_t, JobData> pOngoingJobs;
307  XrdSysRecMutex pMutex;
308 };
309 
310 //------------------------------------------------------------------------------
311 // Check if we support all the specified user options
312 //------------------------------------------------------------------------------
314 {
315  if( config->pHost )
316  {
317  std::cerr << "SOCKS Proxies are not yet supported" << std::endl;
318  return false;
319  }
320 
321  return true;
322 }
323 
324 //------------------------------------------------------------------------------
325 // Append extra cgi info to existing URL
326 //------------------------------------------------------------------------------
327 void AppendCGI( std::string &url, const char *newCGI )
328 {
329  if( !newCGI || !(*newCGI) )
330  return;
331 
332  if( *newCGI == '&' )
333  ++newCGI;
334 
335  if( url.find( '?' ) == std::string::npos )
336  url += "?";
337 
338  if( url.find( '&' ) == std::string::npos )
339  url += "&";
340 
341  url += newCGI;
342 }
343 
344 //------------------------------------------------------------------------------
345 // Process commandline environment settings
346 //------------------------------------------------------------------------------
348 {
350 
351  XrdCpConfig::defVar *cursor = config->intDefs;
352  while( cursor )
353  {
354  env->PutInt( cursor->vName, cursor->intVal );
355  cursor = cursor->Next;
356  }
357 
358  cursor = config->strDefs;
359  while( cursor )
360  {
361  env->PutString( cursor->vName, cursor->strVal );
362  cursor = cursor->Next;
363  }
364 }
365 
366 //------------------------------------------------------------------------------
367 // Translate file type to a string for diagnostics purposes
368 //------------------------------------------------------------------------------
370 {
371  switch( type )
372  {
373  case XrdCpFile::isDir: return "directory";
374  case XrdCpFile::isFile: return "local file";
375  case XrdCpFile::isXroot: return "xroot";
376  case XrdCpFile::isXroots: return "xroots";
377  case XrdCpFile::isHttp: return "http";
378  case XrdCpFile::isHttps: return "https";
379  case XrdCpFile::isStdIO: return "stdio";
380  default: return "other";
381  };
382 }
383 
384 //------------------------------------------------------------------------------
385 // Count the sources
386 //------------------------------------------------------------------------------
387 uint32_t CountSources( XrdCpFile *file )
388 {
389  uint32_t count;
390  for( count = 0; file; file = file->Next, ++count ) {};
391  return count;
392 }
393 
394 //------------------------------------------------------------------------------
395 // Adjust file information for the cases when XrdCpConfig cannot do this
396 //------------------------------------------------------------------------------
398 {
399  //----------------------------------------------------------------------------
400  // If the file is url and the directory offset is not set we set it
401  // to the last slash
402  //----------------------------------------------------------------------------
403  if( file->Doff == 0 )
404  {
405  char *slash = file->Path;
406  for( ; *slash; ++slash ) {};
407  for( ; *slash != '/' && slash > file->Path; --slash ) {};
408  file->Doff = slash - file->Path;
409  }
410 };
411 
412 //------------------------------------------------------------------------------
413 // Recursively index all files and directories inside a remote directory
414 //------------------------------------------------------------------------------
416  std::string basePath,
417  long dirOffset )
418 {
419  using namespace XrdCl;
420 
421  Log *log = DefaultEnv::GetLog();
422  log->Debug( AppMsg, "Indexing %s", basePath.c_str() );
423 
424  DirectoryList *dirList = 0;
425  XRootDStatus st = fs->DirList( URL( basePath ).GetPath(), DirListFlags::Recursive
426  | DirListFlags::Locate | DirListFlags::Merge, dirList );
427  if( !st.IsOK() )
428  {
429  log->Info( AppMsg, "Failed to get directory listing for %s: %s",
430  basePath.c_str(),
431  st.GetErrorMessage().c_str() );
432  return 0;
433  }
434 
435  XrdCpFile start, *current = 0;
436  XrdCpFile *end = &start;
437  int badUrl = 0;
438  for( auto itr = dirList->Begin(); itr != dirList->End(); ++itr )
439  {
440  DirectoryList::ListEntry *e = *itr;
441  if( e->GetStatInfo()->TestFlags( StatInfo::IsDir ) )
442  continue;
443  std::string path = basePath + '/' + e->GetName();
444  current = new XrdCpFile( path.c_str(), badUrl );
445  if( badUrl )
446  {
447  log->Error( AppMsg, "Bad URL: %s", current->Path );
448  delete current;
449  return 0;
450  }
451 
452  current->Doff = dirOffset;
453  end->Next = current;
454  end = current;
455  }
456 
457  delete dirList;
458 
459  return start.Next;
460 }
461 
462 //------------------------------------------------------------------------------
463 // Clean up the copy job descriptors
464 //------------------------------------------------------------------------------
465 void CleanUpResults( std::vector<XrdCl::PropertyList *> &results )
466 {
467  std::vector<XrdCl::PropertyList *>::iterator it;
468  for( it = results.begin(); it != results.end(); ++it )
469  delete *it;
470 }
471 
472 //--------------------------------------------------------------------------
473 // Let the show begin
474 //------------------------------------------------------------------------------
475 int main( int argc, char **argv )
476 {
477  using namespace XrdCl;
478 
479  //----------------------------------------------------------------------------
480  // Configure the copy command, if it returns then everything went well, ugly
481  //----------------------------------------------------------------------------
482  XrdCpConfig config( argv[0] );
483  config.Config( argc, argv, XrdCpConfig::optRmtRec );
484  if( !AllOptionsSupported( &config ) )
485  return 50; // generic error
486  ProcessCommandLineEnv( &config );
487 
488  //----------------------------------------------------------------------------
489  // Set options
490  //----------------------------------------------------------------------------
491  CopyProcess process;
492  Log *log = DefaultEnv::GetLog();
493  if( config.Dlvl )
494  {
495  if( config.Dlvl == 1 ) log->SetLevel( Log::InfoMsg );
496  else if( config.Dlvl == 2 ) log->SetLevel( Log::DebugMsg );
497  else if( config.Dlvl == 3 ) log->SetLevel( Log::DumpMsg );
498  }
499 
500  ProgressDisplay progress;
501  if( config.Want(XrdCpConfig::DoNoPbar) || !isatty( fileno( stdout ) ) )
502  progress.PrintProgressBar( false );
503 
504  bool posc = false;
505  bool force = false;
506  bool coerce = false;
507  bool makedir = false;
508  bool dynSrc = false;
509  bool delegate = false;
510  bool preserveXAttr = false;
511  bool rmOnBadCksum = false;
512  bool continue_ = false;
513  bool recurse = false;
514  bool zipappend = false;
515  bool doserver = false;
516  std::string thirdParty = "none"s;
517  std::string thirdPartyMode = "pull"s;
518 
519  const std::string thirdPartyTokenFile = config.tcpTokenFile ? config.tcpTokenFile : ""s;
520 
521  if( config.Want( XrdCpConfig::DoPosc ) ) posc = true;
522  if( config.Want( XrdCpConfig::DoForce ) ) force = true;
523  if( config.Want( XrdCpConfig::DoCoerce ) ) coerce = true;
524  if( config.Want( XrdCpConfig::DoTpc ) ) thirdParty = "first"s;
525  if( config.Want( XrdCpConfig::DoTpcOnly ) ) thirdParty = "only"s;
526  if( config.Want( XrdCpConfig::DoTpcModePush ) ) thirdPartyMode = "push"s;
527  if( config.Want( XrdCpConfig::DoZipAppend ) ) zipappend = true;
528  if( config.Want( XrdCpConfig::DoServer ) ) doserver = true;
529  if( config.Want( XrdCpConfig::DoTpcDlgt ) )
530  {
531  // the env var is being set already here (we are issuing a stat
532  // inhere and we need the env var when we are establishing the
533  // connection and authenticating), but we are also setting a delegate
534  // parameter for CopyJob so it can be used on its own.
535  DlgEnv::Instance().Enable();
536  delegate = true;
537  }
538  else
539  DlgEnv::Instance().Disable();
540 
541  if( config.Want( XrdCpConfig::DoRecurse ) )
542  {
543  makedir = true;
544  recurse = true;
545  }
546  if( config.Want( XrdCpConfig::DoPath ) ) makedir = true;
547  if( config.Want( XrdCpConfig::DoDynaSrc ) ) dynSrc = true;
548  if( config.Want( XrdCpConfig::DoXAttr ) ) preserveXAttr = true;
549  if( config.Want( XrdCpConfig::DoRmOnBadCksum ) ) rmOnBadCksum = true;
550  if( config.Want( XrdCpConfig::DoContinue ) ) continue_ = true;
551 
552  if( force && continue_ )
553  {
554  std::cerr << "Invalid argument combination: continue + force." << std::endl;
555  return 50;
556  }
557 
558  //----------------------------------------------------------------------------
559  // Checksums
560  //----------------------------------------------------------------------------
561  std::string checkSumType;
562  std::string checkSumPreset;
563  std::string checkSumMode = "none";
564  if( config.Want( XrdCpConfig::DoCksum ) )
565  {
566  checkSumMode = "end2end";
567  std::vector<std::string> ckSumParams;
568  Utils::splitString( ckSumParams, config.CksVal, ":" );
569  if( ckSumParams.size() > 1 )
570  {
571  if( ckSumParams[1] == "print" )
572  {
573  checkSumMode = "target";
574  progress.PrintTargetCheckSum( true );
575  }
576  else
577  checkSumPreset = ckSumParams[1];
578  }
579  checkSumType = ckSumParams[0];
580  }
581 
582  if( config.Want( XrdCpConfig::DoCksrc ) )
583  {
584  checkSumMode = "source";
585  std::vector<std::string> ckSumParams;
586  Utils::splitString( ckSumParams, config.CksVal, ":" );
587  if( ckSumParams.size() == 2 )
588  {
589  checkSumMode = "source";
590  checkSumType = ckSumParams[0];
591  progress.PrintSourceCheckSum( true );
592  }
593  else
594  {
595  std::cerr << "Invalid parameter: " << config.CksVal << std::endl;
596  return 50; // generic error
597  }
598  }
599 
600  if( !config.AddCksVal.empty() )
601  progress.PrintAdditionalCheckSum( true );
602 
603  //----------------------------------------------------------------------------
604  // ZIP archive
605  //----------------------------------------------------------------------------
606  std::string zipFile;
607  bool zip = false;
608  if( config.Want( XrdCpConfig::DoZip ) )
609  {
610  zipFile = config.zipFile;
611  zip = true;
612  }
613 
614  //----------------------------------------------------------------------------
615  // Extreme Copy
616  //----------------------------------------------------------------------------
617  int nbSources = 0;
618  bool xcp = false;
619  if( config.Want( XrdCpConfig::DoSources ) )
620  {
621  nbSources = config.nSrcs;
622  xcp = true;
623  }
624 
625  //----------------------------------------------------------------------------
626  // Environment settings
627  //----------------------------------------------------------------------------
629 
630  /* Stop PostMaster when exiting main() to ensure proper shutdown */
631  struct scope_exit {
632  ~scope_exit() { XrdCl::DefaultEnv::GetPostMaster()->Stop(); }
633  } stopPostMaster;
634 
635  if( config.nStrm != 0 )
636  env->PutInt( "SubStreamsPerChannel", config.nStrm + 1 /*stands for the control stream*/ );
637 
638  if( config.Retry != -1 )
639  {
640  env->PutInt( "CpRetry", config.Retry );
641  env->PutString( "CpRetryPolicy", config.RetryPolicy );
642  }
643 
644  if( config.Want( XrdCpConfig::DoNoTlsOK ) )
645  env->PutInt( "NoTlsOK", 1 );
646 
647  if( config.Want( XrdCpConfig::DoTlsNoData ) )
648  env->PutInt( "TlsNoData", 1 );
649 
650  if( config.Want( XrdCpConfig::DoTlsMLF ) )
651  env->PutInt( "TlsMetalink", 1 );
652 
653  if( config.Want( XrdCpConfig::DoZipMtlnCksum ) )
654  env->PutInt( "ZipMtlnCksum", 1 );
655 
656  //----------------------------------------------------------------------------
657  // Headers to inject into the HTTP requests. These are handed to the XrdClHttp
658  // plug-in verbatim, as a newline separated list; that plug-in owns all of the
659  // parsing and validation of their contents.
660  //----------------------------------------------------------------------------
661  if( !config.HttpHeaders.empty() )
662  {
663  std::string headers;
664  for( auto &header : config.HttpHeaders )
665  {
666  if( !headers.empty() ) headers += '\n';
667  headers += header;
668  }
669  env->PutString( "HttpHeaders", headers );
670  }
671 
672  int chunkSize = DefaultCPChunkSize;
673  env->GetInt( "CPChunkSize", chunkSize );
674 
675  int blockSize = DefaultXCpBlockSize;
676  env->GetInt( "XCpBlockSize", blockSize );
677 
678  int parallelChunks = DefaultCPParallelChunks;
679  env->GetInt( "CPParallelChunks", parallelChunks );
680  if( parallelChunks < 1 ||
681  parallelChunks > std::numeric_limits<uint8_t>::max() )
682  {
683  std::cerr << "Can only handle between 1 and ";
684  std::cerr << (int)std::numeric_limits<uint8_t>::max();
685  std::cerr << " chunks in parallel. You asked for " << parallelChunks;
686  std::cerr << "." << std::endl;
687  return 50; // generic error
688  }
689 
690  if( !preserveXAttr )
691  {
692  int val = DefaultPreserveXAttrs;
693  env->GetInt( "PreserveXAttrs", val );
694  if( val ) preserveXAttr = true;
695  }
696 
697  log->Dump( AppMsg, "Chunk size: %d, parallel chunks %d, streams: %d",
698  chunkSize, parallelChunks, config.nStrm + 1 );
699 
700  //----------------------------------------------------------------------------
701  // Build the URLs
702  //----------------------------------------------------------------------------
703  std::vector<XrdCl::PropertyList*> resultVect;
704 
705  std::string dest;
706  if( config.dstFile->Protocol == XrdCpFile::isDir ||
707  config.dstFile->Protocol == XrdCpFile::isFile )
708  {
709  dest = "file://";
710 
711  // if it is not an absolute path append cwd
712  if( config.dstFile->Path[0] != '/' )
713  {
714  char buf[FILENAME_MAX];
715  char *cwd = getcwd( buf, FILENAME_MAX );
716  if( !cwd )
717  {
718  XRootDStatus st( stError, XProtocol::mapError( errno ), errno );
719  std::cerr << st.GetErrorMessage() << std::endl;
720  return st.GetShellCode();
721  }
722  dest += cwd;
723  dest += '/';
724  }
725  }
726  dest += config.dstFile->Path;
727 
728  //----------------------------------------------------------------------------
729  // We need to check whether our target is a file or a directory:
730  // 1) it's a file, so we can accept only one source
731  // 2) it's a directory, so:
732  // * we can accept multiple sources
733  // * we need to append the source name
734  //----------------------------------------------------------------------------
735  bool targetIsDir = false;
736  bool targetExists = false;
737  if( config.dstFile->Protocol == XrdCpFile::isDir )
738  targetIsDir = true;
739  else if( config.dstFile->Protocol == XrdCpFile::isXroot ||
741  {
742  URL target( dest );
743  FileSystem fs( target );
744  StatInfo *statInfo = 0;
745  XRootDStatus st = fs.Stat( target.GetPathWithParams(), statInfo );
746  if( st.IsOK() )
747  {
748  if( statInfo->TestFlags( StatInfo::IsDir ) )
749  targetIsDir = true;
750  targetExists = true;
751  }
752  else if( st.errNo == kXR_NotFound && makedir )
753  {
754  int n = strlen(config.dstFile->Path);
755  if( config.dstFile->Path[n-1] == '/' )
756  targetIsDir = true;
757  }
758  else if( st.errNo == kXR_NotAuthorized )
759  {
760  log->Error( AppMsg, "%s (destination)", st.ToString().c_str() );
761  std::cerr << st.ToStr() << std::endl;
762  return st.GetShellCode();
763  }
764 
765  delete statInfo;
766  }
767 
768  if( !targetIsDir && targetExists && !force && !recurse && !zipappend )
769  {
770  XRootDStatus st( stError, errInvalidOp, EEXIST );
771  // Unable to create /tmp/test.txt; file exists
772  log->Error( AppMsg, "%s (destination)", st.ToString().c_str() );
773  std::cerr << "Run: " << st.ToStr() << std::endl;
774  return st.GetShellCode();
775  }
776 
777  //----------------------------------------------------------------------------
778  // If we have multiple sources and target is neither a directory nor stdout
779  // then we cannot proceed
780  //----------------------------------------------------------------------------
781  if( CountSources(config.srcFile) > 1 && !targetIsDir &&
782  config.dstFile->Protocol != XrdCpFile::isStdIO )
783  {
784  std::cerr << "Multiple sources were given but target is not a directory.";
785  std::cerr << std::endl;
786  return 50; // generic error
787  }
788 
789  //----------------------------------------------------------------------------
790  // If we're doing remote recursive copy, chain all the files (if it's a
791  // directory)
792  //----------------------------------------------------------------------------
793  bool remoteSrcIsDir = false;
794  if( config.Want( XrdCpConfig::DoRecurse ) &&
795  (config.srcFile->Protocol == XrdCpFile::isXroot ||
796  config.srcFile->Protocol == XrdCpFile::isXroots) )
797  {
798  URL source( config.srcFile->Path );
799  FileSystem *fs = new FileSystem( source );
800  StatInfo *statInfo = 0;
801 
802  XRootDStatus st = fs->Stat( source.GetPath(), statInfo );
803  if( st.IsOK() && statInfo->TestFlags( StatInfo::IsDir ) )
804  {
805  remoteSrcIsDir = true;
806  //------------------------------------------------------------------------
807  // Recursively index the remote directory
808  //------------------------------------------------------------------------
809  delete config.srcFile;
810  std::string url = source.GetURL();
811  config.srcFile = IndexRemote( fs, url, url.size() );
812  if ( !config.srcFile )
813  {
814  std::cerr << "Error indexing remote directory.";
815  return 50; // generic error
816  }
817  }
818 
819  delete fs;
820  delete statInfo;
821  }
822 
823  XrdCpFile *sourceFile = config.srcFile;
824  //----------------------------------------------------------------------------
825  // Process the sources
826  //----------------------------------------------------------------------------
827  while( sourceFile )
828  {
829  AdjustFileInfo( sourceFile );
830 
831  //--------------------------------------------------------------------------
832  // Create a job for every source
833  //--------------------------------------------------------------------------
834  PropertyList properties;
835  PropertyList *results = new PropertyList;
836  std::string source = sourceFile->Path;
837  if( sourceFile->Protocol == XrdCpFile::isFile )
838  {
839  // make sure it is an absolute path
840  if( source[0] == '/' )
841  source = "file://" + source;
842  else
843  {
844  char buf[FILENAME_MAX];
845  char *cwd = getcwd( buf, FILENAME_MAX );
846  if( !cwd )
847  {
848  XRootDStatus st( stError, XProtocol::mapError( errno ), errno );
849  std::cerr << st.GetErrorMessage() << std::endl;
850  return st.GetShellCode();
851  }
852  source = "file://" + std::string( cwd ) + '/' + source;
853  }
854  }
855 
856  AppendCGI( source, config.srcOpq );
857 
858  std::string sourcePathObf = sourceFile->Path;
859  std::string destPathObf = dest;
860  if( unlikely(log->GetLevel() >= Log::DumpMsg) ) {
861  sourcePathObf = obfuscateAuth(sourcePathObf);
862  destPathObf = obfuscateAuth(destPathObf);
863  }
864  log->Dump( AppMsg, "Processing source entry: %s, type %s, target file: %s, logLevel = %d",
865  sourcePathObf.c_str(), FileType2String( sourceFile->Protocol ),
866  destPathObf.c_str(), log->GetLevel() );
867 
868  //--------------------------------------------------------------------------
869  // Set up the job
870  //--------------------------------------------------------------------------
871  std::string target = dest;
872 
873 
874  bool srcIsDir = false;
875  // if this is local file, for a directory Dlen + Doff will overlap with path size
876  if( strncmp( sourceFile->ProtName, "file", 4 ) == 0 )
877  srcIsDir = std::string( sourceFile->Path ).size() == size_t( sourceFile->Doff + sourceFile->Dlen );
878  // otherwise we are handling a remote file
879  else
880  srcIsDir = remoteSrcIsDir;
881  // if this is a recursive copy make sure we preserve the directory structure
882  if( config.Want( XrdCpConfig::DoRecurse ) && srcIsDir )
883  {
884  // get the source directory
885  std::string srcDir( sourceFile->Path, sourceFile->Doff );
886  // remove the trailing slash
887  if( srcDir[srcDir.size() - 1] == '/' )
888  srcDir = srcDir.substr( 0, srcDir.size() - 1 );
889  size_t diroff = srcDir.rfind( '/' );
890  // if there is no '/' it means a directory name has been given as relative path
891  if( diroff == std::string::npos ) diroff = 0;
892  target += '/';
893  target += sourceFile->Path + diroff;
894  // remove the filename from destination path as it will be appended later anyway
895  target = target.substr( 0 , target.rfind('/') );
896  }
897  AppendCGI( target, config.dstOpq );
898 
899  properties.Set( "source", source );
900  properties.Set( "target", target );
901  properties.Set( "force", force );
902  properties.Set( "posc", posc );
903  properties.Set( "coerce", coerce );
904  properties.Set( "makeDir", makedir );
905  properties.Set( "dynamicSource", dynSrc );
906  properties.Set( "thirdParty", thirdParty );
907  properties.Set( "thirdPartyMode", thirdPartyMode );
908  properties.Set( "checkSumMode", checkSumMode );
909  properties.Set( "checkSumType", checkSumType );
910  properties.Set( "checkSumPreset", checkSumPreset );
911  properties.Set( "chunkSize", chunkSize );
912  properties.Set( "parallelChunks", parallelChunks );
913  properties.Set( "zipArchive", zip );
914  properties.Set( "xcp", xcp );
915  properties.Set( "xcpBlockSize", blockSize );
916  properties.Set( "delegate", delegate );
917  properties.Set( "targetIsDir", targetIsDir );
918  properties.Set( "preserveXAttr", preserveXAttr );
919  properties.Set( "xrate", config.xRate );
920  properties.Set( "xrateThreshold", config.xRateThreshold );
921  properties.Set( "rmOnBadCksum", rmOnBadCksum );
922  properties.Set( "continue", continue_ );
923  properties.Set( "zipAppend", zipappend );
924  properties.Set( "addcksums", config.AddCksVal );
925  properties.Set( "doServer", doserver );
926 
927  if( zip )
928  properties.Set( "zipSource", zipFile );
929 
930  if( xcp )
931  properties.Set( "nbXcpSources", nbSources );
932 
933  if( !thirdPartyTokenFile.empty() )
934  properties.Set( "thirdPartyTokenFile", thirdPartyTokenFile );
935 
936  XRootDStatus st = process.AddJob( properties, results );
937  if( !st.IsOK() )
938  {
939  std::cerr << "AddJob " << source << " -> " << target << ": ";
940  std::cerr << st.ToStr() << std::endl;
941  }
942  resultVect.push_back( results );
943  sourceFile = sourceFile->Next;
944  }
945 
946  //----------------------------------------------------------------------------
947  // Configure the copy process
948  //----------------------------------------------------------------------------
949  PropertyList processConfig;
950  processConfig.Set( "jobType", "configuration" );
951  processConfig.Set( "parallel", config.Parallel );
952  process.AddJob( processConfig, 0 );
953 
954  //----------------------------------------------------------------------------
955  // Prepare and run the copy process
956  //----------------------------------------------------------------------------
957  XRootDStatus st = process.Prepare();
958  if( !st.IsOK() )
959  {
960  CleanUpResults( resultVect );
961  std::cerr << "Prepare: " << st.ToStr() << std::endl;
962  return st.GetShellCode();
963  }
964 
965  st = process.Run( &progress );
966  if( !st.IsOK() )
967  {
968  if( resultVect.size() == 1 )
969  std::cerr << "Run: " << st.ToStr() << std::endl;
970  else
971  {
972  std::vector<XrdCl::PropertyList*>::iterator it;
973  uint32_t i = 1;
974  uint32_t jobsRun = 0;
975  uint32_t errors = 0;
976  for( it = resultVect.begin(); it != resultVect.end(); ++it, ++i )
977  {
978  if( !(*it)->HasProperty( "status" ) )
979  continue;
980 
981  XRootDStatus st = (*it)->Get<XRootDStatus>("status");
982  if( !st.IsOK() )
983  {
984  std::cerr << "Job #" << i << ": " << st.ToStr();
985  ++errors;
986  }
987  ++jobsRun;
988  }
989  std::cerr << "Jobs total: " << resultVect.size();
990  std::cerr << ", run: " << jobsRun;
991  std::cerr << ", errors: " << errors << std::endl;
992  }
993  CleanUpResults( resultVect );
994  return st.GetShellCode();
995  }
996  CleanUpResults( resultVect );
997  return 0;
998 }
999 
@ kXR_NotAuthorized
Definition: XProtocol.hh:1042
@ kXR_NotFound
Definition: XProtocol.hh:1043
bool AllOptionsSupported(XrdCpConfig *config)
Definition: XrdClCopy.cc:313
const char * FileType2String(XrdCpFile::PType type)
Definition: XrdClCopy.cc:369
int main(int argc, char **argv)
Definition: XrdClCopy.cc:475
XrdCpFile * IndexRemote(XrdCl::FileSystem *fs, std::string basePath, long dirOffset)
Definition: XrdClCopy.cc:415
void ProcessCommandLineEnv(XrdCpConfig *config)
Definition: XrdClCopy.cc:347
void CleanUpResults(std::vector< XrdCl::PropertyList * > &results)
Definition: XrdClCopy.cc:465
void AdjustFileInfo(XrdCpFile *file)
Definition: XrdClCopy.cc:397
uint32_t CountSources(XrdCpFile *file)
Definition: XrdClCopy.cc:387
void AppendCGI(std::string &url, const char *newCGI)
Definition: XrdClCopy.cc:327
#define unlikely(x)
std::string obfuscateAuth(const std::string &input)
void PrintAdditionalCheckSum(bool print)
Definition: XrdClCopy.cc:287
void PrintSourceCheckSum(bool print)
Definition: XrdClCopy.cc:285
void PrintProgressBar(bool print)
Definition: XrdClCopy.cc:284
void PrintCheckSum(const XrdCl::URL *url, const std::string &checkSum, uint64_t size)
Print the checksum.
Definition: XrdClCopy.cc:259
virtual void BeginJob(uint32_t jobNum, uint32_t jobTotal, const XrdCl::URL *source, const XrdCl::URL *destination)
Begin job.
Definition: XrdClCopy.cc:64
std::string GetProgressBar(time_t now)
Get progress bar.
Definition: XrdClCopy.cc:151
std::string GetSummaryBar(time_t now)
Get sumary bar.
Definition: XrdClCopy.cc:193
ProgressDisplay()
Constructor.
Definition: XrdClCopy.cc:56
virtual void EndJob(uint32_t jobNum, const XrdCl::PropertyList *results)
End job.
Definition: XrdClCopy.cc:91
void PrintTargetCheckSum(bool print)
Definition: XrdClCopy.cc:286
virtual void JobProgress(uint32_t jobNum, uint64_t bytesProcessed, uint64_t bytesTotal)
Job progress.
Definition: XrdClCopy.cc:224
static int mapError(int rc)
Definition: XProtocol.hh:1404
Copy the data from one point to another.
XRootDStatus Run(CopyProgressHandler *handler)
Run the copy jobs.
XRootDStatus Prepare()
XRootDStatus AddJob(const PropertyList &properties, PropertyList *results)
Interface for copy progress notification.
static PostMaster * GetPostMaster()
Get default post master.
static Env * GetEnv()
Get default client environment.
const std::string & GetName() const
Get file name.
StatInfo * GetStatInfo()
Get the stat info object.
Iterator End()
Get the end iterator.
Iterator Begin()
Get the begin iterator.
bool PutInt(const std::string &key, int value)
Definition: XrdClEnv.cc:136
bool PutString(const std::string &key, const std::string &value)
Definition: XrdClEnv.cc:52
bool GetInt(const std::string &key, int &value)
Definition: XrdClEnv.cc:115
Send file/filesystem queries to an XRootD cluster.
XRootDStatus DirList(const std::string &path, DirListFlags::Flags flags, ResponseHandler *handler, time_t timeout=0) XRD_WARN_UNUSED_RESULT
XRootDStatus Stat(const std::string &path, ResponseHandler *handler, time_t timeout=0) XRD_WARN_UNUSED_RESULT
Handle diagnostics.
Definition: XrdClLog.hh:101
void SetLevel(LogLevel level)
Set the level of the messages that should be sent to the destination.
Definition: XrdClLog.hh:193
void Error(uint64_t topic, const char *format,...)
Report an error.
Definition: XrdClLog.cc:231
LogLevel GetLevel() const
Get the log level.
Definition: XrdClLog.hh:258
void Dump(uint64_t topic, const char *format,...)
Print a dump message.
Definition: XrdClLog.cc:299
void Info(uint64_t topic, const char *format,...)
Print an info.
Definition: XrdClLog.cc:265
void Debug(uint64_t topic, const char *format,...)
Print a debug message.
Definition: XrdClLog.cc:282
bool Stop()
Stop the postmaster.
A key-value pair map storing both keys and values as strings.
void Set(const std::string &name, const Item &value)
bool Get(const std::string &name, Item &item) const
Object stat info.
bool TestFlags(uint32_t flags) const
Test flags.
URL representation.
Definition: XrdClURL.hh:31
std::string GetHostId() const
Get the host part of the URL (user:password@host:port)
Definition: XrdClURL.hh:99
const std::string & GetProtocol() const
Get the protocol.
Definition: XrdClURL.hh:118
std::string GetPathWithParams() const
Get the path with params.
Definition: XrdClURL.cc:294
std::string GetURL() const
Get the URL.
Definition: XrdClURL.hh:86
bool IsLocalFile() const
Definition: XrdClURL.cc:450
const std::string & GetPath() const
Get the path.
Definition: XrdClURL.hh:217
static std::string BytesToString(uint64_t bytes)
Convert bytes to a human readable string.
Definition: XrdClUtils.cc:367
const std::string & GetErrorMessage() const
Get error message.
std::string ToStr() const
Convert to string.
short Doff
Definition: XrdCpFile.hh:46
PType Protocol
Definition: XrdCpFile.hh:49
char * Path
Definition: XrdCpFile.hh:45
char ProtName[8]
Definition: XrdCpFile.hh:50
XrdCpFile * Next
Definition: XrdCpFile.hh:44
short Dlen
Definition: XrdCpFile.hh:47
const int DefaultCPChunkSize
const uint16_t stError
An error occurred that could potentially be retried.
Definition: XrdClStatus.hh:32
const uint64_t AppMsg
LocateImpl< false > Locate
const uint16_t errInvalidOp
Definition: XrdClStatus.hh:51
const int DefaultCPParallelChunks
const int DefaultXCpBlockSize
const int DefaultPreserveXAttrs
bool IsOK() const
We're fine.
Definition: XrdClStatus.hh:124
std::string ToString() const
Create a string representation.
Definition: XrdClStatus.cc:97
uint32_t errNo
Errno, if any.
Definition: XrdClStatus.hh:148
int GetShellCode() const
Get the status code that may be returned to the shell.
Definition: XrdClStatus.hh:129
const char * vName
Definition: XrdCpConfig.hh:53
defVar * intDefs
Definition: XrdCpConfig.hh:63
void Config(int argc, char **argv, int Opts=0)
Definition: XrdCpConfig.cc:210
char * tcpTokenFile
Definition: XrdCpConfig.hh:95
std::vector< std::string > AddCksVal
Definition: XrdCpConfig.hh:99
const char * dstOpq
Definition: XrdCpConfig.hh:65
static const uint64_t DoZipMtlnCksum
Definition: XrdCpConfig.hh:196
XrdCpFile * srcFile
Definition: XrdCpConfig.hh:90
XrdCpFile * dstFile
Definition: XrdCpConfig.hh:91
char * zipFile
Definition: XrdCpConfig.hh:93
static const uint64_t DoNoPbar
Definition: XrdCpConfig.hh:129
static const uint64_t DoCoerce
Definition: XrdCpConfig.hh:112
static const uint64_t DoForce
Definition: XrdCpConfig.hh:118
static const uint64_t DoRmOnBadCksum
Definition: XrdCpConfig.hh:199
static const uint64_t DoNoTlsOK
Definition: XrdCpConfig.hh:184
static const uint64_t DoTpc
Definition: XrdCpConfig.hh:157
char * pHost
Definition: XrdCpConfig.hh:71
std::vector< std::string > HttpHeaders
Definition: XrdCpConfig.hh:101
static const uint64_t DoCksum
Definition: XrdCpConfig.hh:108
defVar * strDefs
Definition: XrdCpConfig.hh:64
static const uint64_t DoCksrc
Definition: XrdCpConfig.hh:107
static const uint64_t DoTpcModePush
Definition: XrdCpConfig.hh:218
static const uint64_t DoTpcDlgt
Definition: XrdCpConfig.hh:159
static const uint64_t DoZip
Definition: XrdCpConfig.hh:178
static const uint64_t DoContinue
Definition: XrdCpConfig.hh:202
const char * CksVal
Definition: XrdCpConfig.hh:88
static const uint64_t DoRecurse
Definition: XrdCpConfig.hh:139
const char * srcOpq
Definition: XrdCpConfig.hh:66
static const uint64_t DoZipAppend
Definition: XrdCpConfig.hh:211
static const uint64_t DoDynaSrc
Definition: XrdCpConfig.hh:173
int Want(uint64_t What)
Definition: XrdCpConfig.hh:234
long long xRate
Definition: XrdCpConfig.hh:68
static const uint64_t DoSources
Definition: XrdCpConfig.hh:151
static const uint64_t DoXAttr
Definition: XrdCpConfig.hh:193
static const uint64_t DoTlsMLF
Definition: XrdCpConfig.hh:187
static const int optRmtRec
Definition: XrdCpConfig.hh:226
std::string RetryPolicy
Definition: XrdCpConfig.hh:78
static const uint64_t DoPath
Definition: XrdCpConfig.hh:190
static const uint64_t DoPosc
Definition: XrdCpConfig.hh:132
long long xRateThreshold
Definition: XrdCpConfig.hh:69
static const uint64_t DoTpcOnly
Definition: XrdCpConfig.hh:158
static const uint64_t DoTlsNoData
Definition: XrdCpConfig.hh:181
static const uint64_t DoServer
Definition: XrdCpConfig.hh:145