XRootD
Loading...
Searching...
No Matches
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
26#include "XrdApps/XrdCpFile.hh"
30#include "XrdCl/XrdClLog.hh"
32#include "XrdCl/XrdClUtils.hh"
33#include "XrdCl/XrdClDlgEnv.hh"
35#include "XrdSys/XrdSysE2T.hh"
38
39#include <cstdio>
40#include <iostream>
41#include <iomanip>
42#include <limits>
43#include <string>
44
45using 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//------------------------------------------------------------------------------
327void 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//------------------------------------------------------------------------------
387uint32_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
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;
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//------------------------------------------------------------------------------
465void 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//------------------------------------------------------------------------------
475int 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 ||
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 &&
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 ||
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
@ kXR_NotFound
bool AllOptionsSupported(XrdCpConfig *config)
Definition XrdClCopy.cc:313
const char * FileType2String(XrdCpFile::PType type)
Definition XrdClCopy.cc:369
void ProcessCommandLineEnv(XrdCpConfig *config)
Definition XrdClCopy.cc:347
void CleanUpResults(std::vector< XrdCl::PropertyList * > &results)
Definition XrdClCopy.cc:465
XrdCpFile * IndexRemote(XrdCl::FileSystem *fs, std::string basePath, long dirOffset)
Definition XrdClCopy.cc:415
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)
int main(int argc, char *argv[])
Definition XrdMain.cc:161
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)
Copy the data from one point to another.
XRootDStatus Run(CopyProgressHandler *handler)
Run the copy jobs.
XRootDStatus AddJob(const PropertyList &properties, PropertyList *results)
Interface for copy progress notification.
static Log * GetLog()
Get default log.
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.
static DlgEnv & Instance()
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
Handle diagnostics.
Definition XrdClLog.hh:101
@ InfoMsg
print info
Definition XrdClLog.hh:111
@ DebugMsg
print debug info
Definition XrdClLog.hh:112
@ DumpMsg
print details of the request and responses
Definition XrdClLog.hh:113
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.
@ IsDir
This is a directory.
URL representation.
Definition XrdClURL.hh:31
const std::string & GetPath() const
Get the path.
Definition XrdClURL.hh:217
std::string GetHostId() const
Get the host part of the URL (user:password@host:port).
Definition XrdClURL.hh:99
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 & GetProtocol() const
Get the protocol.
Definition XrdClURL.hh:118
static void splitString(Container &result, const std::string &input, const std::string &delimiter)
Split a string.
Definition XrdClUtils.hh:56
static std::string BytesToString(uint64_t bytes)
Convert bytes to a human readable string.
const std::string & GetErrorMessage() const
Get error message.
std::string ToStr() const
Convert to string.
defVar * intDefs
void Config(int argc, char **argv, int Opts=0)
char * tcpTokenFile
std::vector< std::string > AddCksVal
const char * dstOpq
static const uint64_t DoZipMtlnCksum
XrdCpFile * srcFile
XrdCpFile * dstFile
char * zipFile
static const uint64_t DoNoPbar
static const uint64_t DoCoerce
static const uint64_t DoForce
static const uint64_t DoRmOnBadCksum
static const uint64_t DoNoTlsOK
static const uint64_t DoTpc
std::vector< std::string > HttpHeaders
static const uint64_t DoCksum
defVar * strDefs
static const uint64_t DoCksrc
static const uint64_t DoTpcModePush
static const uint64_t DoTpcDlgt
static const uint64_t DoZip
static const uint64_t DoContinue
const char * CksVal
static const uint64_t DoRecurse
const char * srcOpq
static const uint64_t DoZipAppend
static const uint64_t DoDynaSrc
int Want(uint64_t What)
long long xRate
static const uint64_t DoSources
static const uint64_t DoXAttr
static const uint64_t DoTlsMLF
static const int optRmtRec
std::string RetryPolicy
static const uint64_t DoPath
static const uint64_t DoPosc
long long xRateThreshold
static const uint64_t DoTpcOnly
static const uint64_t DoTlsNoData
static const uint64_t DoServer
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.
const uint64_t AppMsg
const uint16_t errInvalidOp
const int DefaultCPParallelChunks
const int DefaultXCpBlockSize
const int DefaultPreserveXAttrs
@ Merge
Merge duplicates.
@ Recursive
Do a recursive listing.
bool IsOK() const
We're fine.
std::string ToString() const
Create a string representation.
uint32_t errNo
Errno, if any.
int GetShellCode() const
Get the status code that may be returned to the shell.
const char * vName