diff --git a/ChangeLog b/ChangeLog index 146392b2..51ab5b19 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,35 @@ +2009-01-12 Tatsuhiro Tsujikawa + + Applied exit-status patch from Pascal Rigaux at Mandriva. aria2 + now returns last error encountered in the HTTP/FTP downloads as a + exit status value. If all downloads finished successfully, aria2 + returns 0. The error code is defined in src/DownloadResult.h. + The error occurred in the download currently in progress is not + reported as a last error. If no error has encountered but there + are in progress or waiting downloads, aria2 returns 7. + + * src/AbstractCommand.cc + * src/DlAbortEx.h + * src/DlRetryEx.h + * src/DownloadCommand.cc + * src/DownloadFailureException.h + * src/DownloadResult.h + * src/FtpNegotiationCommand.cc + * src/HttpSkipResponseCommand.cc + * src/Makefile.am + * src/MultiUrlRequestInfo.cc + * src/MultiUrlRequestInfo.h + * src/RecoverableException.h + * src/RequestGroup.cc + * src/RequestGroup.h + * src/RequestGroupMan.cc + * src/RequestGroupMan.h + * src/URIResult.cc + * src/URIResult.h + * src/main.cc + * src/option_processing.cc + * test/RequestGroupTest.cc + 2009-01-06 Tatsuhiro Tsujikawa Applied AdaptiveURISelector patch from Aurelien Lefebvre. This diff --git a/src/AbstractCommand.cc b/src/AbstractCommand.cc index 5c2f3491..9ec238e0 100644 --- a/src/AbstractCommand.cc +++ b/src/AbstractCommand.cc @@ -149,13 +149,14 @@ bool AbstractCommand::execute() { req->getProtocol()); ss->setError(); - throw DlRetryEx(EX_TIME_OUT); + throw DlRetryEx(EX_TIME_OUT, DownloadResult::TIME_OUT); } e->commands.push_back(this); return false; } } catch(DlAbortEx& err) { logger->error(MSG_DOWNLOAD_ABORTED, err, cuid, req->getUrl().c_str()); + _requestGroup->addURIResult(req->getUrl(), err.getCode()); onAbort(); req->resetUrl(); tryReserved(); @@ -174,6 +175,7 @@ bool AbstractCommand::execute() { if(isAbort) { logger->info(MSG_MAX_TRY, cuid, req->getTryCount()); logger->error(MSG_DOWNLOAD_ABORTED, err, cuid, req->getUrl().c_str()); + _requestGroup->addURIResult(req->getUrl(), err.getCode()); tryReserved(); return true; } else { @@ -181,6 +183,7 @@ bool AbstractCommand::execute() { } } catch(DownloadFailureException& err) { logger->error(EX_EXCEPTION_CAUGHT, err); + _requestGroup->addURIResult(req->getUrl(), err.getCode()); _requestGroup->setHaltRequested(true); return true; } diff --git a/src/DlAbortEx.h b/src/DlAbortEx.h index 9f10305a..86da96cd 100644 --- a/src/DlAbortEx.h +++ b/src/DlAbortEx.h @@ -50,6 +50,8 @@ public: DlAbortEx(const std::string& msg, const Exception& cause):RecoverableException(msg, cause) {} DlAbortEx(const RecoverableException& e):RecoverableException(e) {} + DlAbortEx(const std::string& msg, DownloadResult::RESULT code): + RecoverableException(msg, code) {} }; } // namespace aria2 diff --git a/src/DlRetryEx.h b/src/DlRetryEx.h index caaf8ea5..4a6dffba 100644 --- a/src/DlRetryEx.h +++ b/src/DlRetryEx.h @@ -50,6 +50,8 @@ public: DlRetryEx(const std::string& msg, const Exception& cause):RecoverableException(msg, cause) {} DlRetryEx(const DlRetryEx& e):RecoverableException(e) {} + DlRetryEx(const std::string& msg, DownloadResult::RESULT code): + RecoverableException(msg, code) {} }; } // namespace aria2 diff --git a/src/DownloadCommand.cc b/src/DownloadCommand.cc index 7ef36a50..56c358da 100644 --- a/src/DownloadCommand.cc +++ b/src/DownloadCommand.cc @@ -243,7 +243,7 @@ void DownloadCommand::checkLowestDownloadSpeed() const throw DlAbortEx(StringFormat(EX_TOO_SLOW_DOWNLOAD_SPEED, nowSpeed, lowestDownloadSpeedLimit, - req->getHost().c_str()).str()); + req->getHost().c_str()).str(), DownloadResult::TOO_SLOW_DOWNLOAD_SPEED); } } } diff --git a/src/DownloadFailureException.h b/src/DownloadFailureException.h index d60231d7..c5fd32b0 100644 --- a/src/DownloadFailureException.h +++ b/src/DownloadFailureException.h @@ -52,8 +52,12 @@ protected: public: DownloadFailureException(const std::string& msg):RecoverableException(msg) {} DownloadFailureException(const std::string& msg, - const Exception& cause):RecoverableException(msg, cause) {} - DownloadFailureException(const DownloadFailureException& e):RecoverableException(e) {} + const Exception& cause): + RecoverableException(msg, cause) {} + DownloadFailureException(const DownloadFailureException& e): + RecoverableException(e) {} + DownloadFailureException(const std::string& msg, DownloadResult::RESULT code): + RecoverableException(msg, code) {} }; } // namespace aria2 diff --git a/src/DownloadResult.h b/src/DownloadResult.h index 92fe1b84..d266a3d0 100644 --- a/src/DownloadResult.h +++ b/src/DownloadResult.h @@ -36,18 +36,28 @@ #define _D_DOWNLOAD_RESULT_H_ #include "common.h" -#include "SharedHandle.h" + #include + #include +#include "SharedHandle.h" + namespace aria2 { class DownloadResult { public: + // RESULT is used as an exit status code. enum RESULT { - FINISHED, - NOT_YET, + FINISHED = 0, + UNKNOWN_ERROR = 1, + TIME_OUT = 2, + RESOURCE_NOT_FOUND = 3, + MAX_FILE_NOT_FOUND = 4, + TOO_SLOW_DOWNLOAD_SPEED = 5, + NETWORK_PROBLEM = 6, + IN_PROGRESS = 7, }; int32_t gid; diff --git a/src/FtpNegotiationCommand.cc b/src/FtpNegotiationCommand.cc index 654bc7ed..b1a730d4 100644 --- a/src/FtpNegotiationCommand.cc +++ b/src/FtpNegotiationCommand.cc @@ -261,7 +261,10 @@ bool FtpNegotiationCommand::recvCwd() { if(status != 250) { poolConnection(); _requestGroup->increaseAndValidateFileNotFoundCount(); - throw DlAbortEx(StringFormat(EX_BAD_STATUS, status).str()); + if (status == 550) + throw DlAbortEx(MSG_RESOURCE_NOT_FOUND, DownloadResult::RESOURCE_NOT_FOUND); + else + throw DlAbortEx(StringFormat(EX_BAD_STATUS, status).str()); } if(e->option->getAsBool(PREF_REMOTE_TIME)) { sequence = SEQ_SEND_MDTM; @@ -544,7 +547,10 @@ bool FtpNegotiationCommand::recvRetr() { } if(status != 150 && status != 125) { _requestGroup->increaseAndValidateFileNotFoundCount(); - throw DlAbortEx(StringFormat(EX_BAD_STATUS, status).str()); + if (status == 550) + throw DlAbortEx(MSG_RESOURCE_NOT_FOUND, DownloadResult::RESOURCE_NOT_FOUND); + else + throw DlAbortEx(StringFormat(EX_BAD_STATUS, status).str()); } if(e->option->getAsBool(PREF_FTP_PASV)) { sequence = SEQ_NEGOTIATION_COMPLETED; diff --git a/src/HttpSkipResponseCommand.cc b/src/HttpSkipResponseCommand.cc index f1ae1f03..358217c9 100644 --- a/src/HttpSkipResponseCommand.cc +++ b/src/HttpSkipResponseCommand.cc @@ -160,7 +160,8 @@ bool HttpSkipResponseCommand::processResponse() if(_httpResponse->getResponseStatus() == HttpHeader::S401) { throw DlAbortEx(EX_AUTH_FAILED); }else if(_httpResponse->getResponseStatus() == HttpHeader::S404) { - throw DlAbortEx(MSG_RESOURCE_NOT_FOUND); + throw DlAbortEx(MSG_RESOURCE_NOT_FOUND, + DownloadResult::RESOURCE_NOT_FOUND); } else { throw DlAbortEx(StringFormat(EX_BAD_STATUS, Util::parseUInt(_httpResponse->getResponseStatus())).str()); } diff --git a/src/Makefile.am b/src/Makefile.am index 53633eea..7739de9a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -188,14 +188,15 @@ SRCS = Socket.h\ ServerStat.cc ServerStat.h\ ServerStatMan.cc ServerStatMan.h\ URISelector.h\ - AdaptiveURISelector.cc AdaptiveURISelector.h\ + AdaptiveURISelector.cc AdaptiveURISelector.h\ InOrderURISelector.cc InOrderURISelector.h\ ServerStatURISelector.cc ServerStatURISelector.h\ NsCookieParser.cc NsCookieParser.h\ CookieStorage.cc CookieStorage.h\ SocketBuffer.cc SocketBuffer.h\ OptionHandlerException.cc OptionHandlerException.h\ - bencode.cc bencode.h + bencode.cc bencode.h\ + URIResult.cc URIResult.h if ENABLE_SSL SRCS += TLSContext.h diff --git a/src/Makefile.in b/src/Makefile.in index aff2e2ca..60891f0b 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -411,10 +411,10 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \ ServerStatURISelector.h NsCookieParser.cc NsCookieParser.h \ CookieStorage.cc CookieStorage.h SocketBuffer.cc \ SocketBuffer.h OptionHandlerException.cc \ - OptionHandlerException.h bencode.cc bencode.h TLSContext.h \ - LibgnutlsTLSContext.cc LibgnutlsTLSContext.h \ - LibsslTLSContext.cc LibsslTLSContext.h GZipDecoder.cc \ - GZipDecoder.h Sqlite3MozCookieParser.cc \ + OptionHandlerException.h bencode.cc bencode.h URIResult.cc \ + URIResult.h TLSContext.h LibgnutlsTLSContext.cc \ + LibgnutlsTLSContext.h LibsslTLSContext.cc LibsslTLSContext.h \ + GZipDecoder.cc GZipDecoder.h Sqlite3MozCookieParser.cc \ Sqlite3MozCookieParser.h AsyncNameResolver.cc \ AsyncNameResolver.h IteratableChunkChecksumValidator.cc \ IteratableChunkChecksumValidator.h \ @@ -802,13 +802,14 @@ am__objects_21 = SocketCore.$(OBJEXT) Command.$(OBJEXT) \ InOrderURISelector.$(OBJEXT) ServerStatURISelector.$(OBJEXT) \ NsCookieParser.$(OBJEXT) CookieStorage.$(OBJEXT) \ SocketBuffer.$(OBJEXT) OptionHandlerException.$(OBJEXT) \ - bencode.$(OBJEXT) $(am__objects_1) $(am__objects_2) \ - $(am__objects_3) $(am__objects_4) $(am__objects_5) \ - $(am__objects_6) $(am__objects_7) $(am__objects_8) \ - $(am__objects_9) $(am__objects_10) $(am__objects_11) \ - $(am__objects_12) $(am__objects_13) $(am__objects_14) \ - $(am__objects_15) $(am__objects_16) $(am__objects_17) \ - $(am__objects_18) $(am__objects_19) $(am__objects_20) + bencode.$(OBJEXT) URIResult.$(OBJEXT) $(am__objects_1) \ + $(am__objects_2) $(am__objects_3) $(am__objects_4) \ + $(am__objects_5) $(am__objects_6) $(am__objects_7) \ + $(am__objects_8) $(am__objects_9) $(am__objects_10) \ + $(am__objects_11) $(am__objects_12) $(am__objects_13) \ + $(am__objects_14) $(am__objects_15) $(am__objects_16) \ + $(am__objects_17) $(am__objects_18) $(am__objects_19) \ + $(am__objects_20) am_libaria2c_a_OBJECTS = $(am__objects_21) libaria2c_a_OBJECTS = $(am_libaria2c_a_OBJECTS) am__installdirs = "$(DESTDIR)$(bindir)" @@ -1133,14 +1134,14 @@ SRCS = Socket.h SocketCore.cc SocketCore.h BinaryStream.h Command.cc \ ServerStatURISelector.h NsCookieParser.cc NsCookieParser.h \ CookieStorage.cc CookieStorage.h SocketBuffer.cc \ SocketBuffer.h OptionHandlerException.cc \ - OptionHandlerException.h bencode.cc bencode.h $(am__append_1) \ - $(am__append_2) $(am__append_3) $(am__append_4) \ - $(am__append_5) $(am__append_6) $(am__append_7) \ - $(am__append_8) $(am__append_9) $(am__append_10) \ - $(am__append_11) $(am__append_12) $(am__append_13) \ - $(am__append_14) $(am__append_15) $(am__append_16) \ - $(am__append_17) $(am__append_18) $(am__append_19) \ - $(am__append_20) + OptionHandlerException.h bencode.cc bencode.h URIResult.cc \ + URIResult.h $(am__append_1) $(am__append_2) $(am__append_3) \ + $(am__append_4) $(am__append_5) $(am__append_6) \ + $(am__append_7) $(am__append_8) $(am__append_9) \ + $(am__append_10) $(am__append_11) $(am__append_12) \ + $(am__append_13) $(am__append_14) $(am__append_15) \ + $(am__append_16) $(am__append_17) $(am__append_18) \ + $(am__append_19) $(am__append_20) noinst_LIBRARIES = libaria2c.a libaria2c_a_SOURCES = $(SRCS) aria2c_LDADD = libaria2c.a @LIBINTL@ @ALLOCA@ @LIBGNUTLS_LIBS@\ @@ -1499,6 +1500,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TimedHaltCommand.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TrackerWatcherCommand.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TransferStat.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/URIResult.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/URLMetalinkParserState.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UTPexExtensionMessage.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UnknownLengthPieceStorage.Po@am__quote@ diff --git a/src/MultiUrlRequestInfo.cc b/src/MultiUrlRequestInfo.cc index ddd1a87e..192eb128 100644 --- a/src/MultiUrlRequestInfo.cc +++ b/src/MultiUrlRequestInfo.cc @@ -99,9 +99,9 @@ void MultiUrlRequestInfo::printMessageForContinue() << "\n"; } -int MultiUrlRequestInfo::execute() +DownloadResult::RESULT MultiUrlRequestInfo::execute() { - int returnValue = 1; + DownloadResult::RESULT returnValue = DownloadResult::FINISHED; try { DownloadEngineHandle e = DownloadEngineFactory().newDownloadEngine(_option, _requestGroups); @@ -190,10 +190,14 @@ int MultiUrlRequestInfo::execute() _summaryOut << std::flush; RequestGroupMan::DownloadStat s = e->_requestGroupMan->getDownloadStat(); - if(s.allCompleted()) { - returnValue = 0; - } else { + if(!s.allCompleted()) { printMessageForContinue(); + if(s.getLastErrorResult() == DownloadResult::FINISHED && + s.getInProgress() > 0) { + returnValue = DownloadResult::IN_PROGRESS; + } else { + returnValue = s.getLastErrorResult(); + } } } catch(RecoverableException& e) { _logger->error(EX_EXCEPTION_CAUGHT, e); diff --git a/src/MultiUrlRequestInfo.h b/src/MultiUrlRequestInfo.h index ccf37346..25742034 100644 --- a/src/MultiUrlRequestInfo.h +++ b/src/MultiUrlRequestInfo.h @@ -36,10 +36,13 @@ #define _D_MULTI_URL_REQUEST_INFO_H_ #include "common.h" -#include "SharedHandle.h" + #include #include +#include "SharedHandle.h" +#include "DownloadResult.h" + namespace aria2 { class RequestGroup; @@ -71,9 +74,10 @@ public: virtual ~MultiUrlRequestInfo(); /** - * Returns 0 if all downloads have completed, otherwise returns 1. + * Returns FINISHED if all downloads have completed, otherwise returns the + * last download result. */ - int execute(); + DownloadResult::RESULT execute(); }; typedef SharedHandle MultiUrlRequestInfoHandle; diff --git a/src/RecoverableException.h b/src/RecoverableException.h index 664ddc48..3064a220 100644 --- a/src/RecoverableException.h +++ b/src/RecoverableException.h @@ -35,10 +35,14 @@ #ifndef _D_RECOVERABLE_EXCEPTION_H_ #define _D_RECOVERABLE_EXCEPTION_H_ #include "Exception.h" +#include "DownloadResult.h" namespace aria2 { class RecoverableException:public Exception { +private: + DownloadResult::RESULT _code; + protected: virtual SharedHandle copy() const { @@ -46,10 +50,22 @@ protected: return e; } public: - RecoverableException(const std::string& msg):Exception(msg) {} - RecoverableException(const std::string& msg, - const Exception& cause):Exception(msg, cause) {} - RecoverableException(const RecoverableException& e):Exception(e) {} + RecoverableException(const std::string& msg): + Exception(msg), + _code(DownloadResult::UNKNOWN_ERROR) {} + + RecoverableException(const std::string& msg, const Exception& cause): + Exception(msg, cause), + _code(DownloadResult::UNKNOWN_ERROR) {} + + RecoverableException(const RecoverableException& e): + Exception(e), + _code(DownloadResult::UNKNOWN_ERROR) {} + + RecoverableException(const std::string& msg, DownloadResult::RESULT result): + Exception(msg), _code(result) {} + + DownloadResult::RESULT getCode() const { return _code; } }; } // namespace aria2 diff --git a/src/RequestGroup.cc b/src/RequestGroup.cc index 57b421fc..e2550776 100644 --- a/src/RequestGroup.cc +++ b/src/RequestGroup.cc @@ -64,7 +64,6 @@ #include "RequestGroupMan.h" #include "DefaultBtProgressInfoFile.h" #include "DefaultPieceStorage.h" -#include "DownloadResult.h" #include "DownloadHandlerFactory.h" #include "MemoryBufferPreDownloadHandler.h" #include "DownloadHandlerConstants.h" @@ -171,6 +170,19 @@ bool RequestGroup::allDownloadFinished() const } } +DownloadResult::RESULT RequestGroup::downloadResult() const +{ + if (downloadFinished()) + return DownloadResult::FINISHED; + else { + if (_uriResults.empty()) { + return DownloadResult::UNKNOWN_ERROR; + } else { + return _uriResults.back().getResult(); + } + } +} + void RequestGroup::closeFile() { if(!_pieceStorage.isNull()) { @@ -902,9 +914,7 @@ DownloadResultHandle RequestGroup::createDownloadResult() const uris.size(), sessionDownloadLength, _downloadContext->calculateSessionTime(), - downloadFinished()? - DownloadResult::FINISHED : - DownloadResult::NOT_YET)); + downloadResult())); } void RequestGroup::registerServerHost(const ServerHostHandle& serverHost) @@ -1073,7 +1083,8 @@ void RequestGroup::increaseAndValidateFileNotFoundCount() if(maxCount > 0 && _fileNotFoundCount >= maxCount && _segmentMan->calculateSessionDownloadLength() == 0) { throw DownloadFailureException - (StringFormat("Reached max-file-not-found count=%u", maxCount).str()); + (StringFormat("Reached max-file-not-found count=%u", maxCount).str(), + DownloadResult::MAX_FILE_NOT_FOUND); } } @@ -1097,4 +1108,14 @@ void RequestGroup::tuneDownloadCommand(DownloadCommand* command) _uriSelector->tuneDownloadCommand(_uris, command); } +void RequestGroup::addURIResult(std::string uri, DownloadResult::RESULT result) +{ + _uriResults.push_back(URIResult(uri, result)); +} + +const std::deque& RequestGroup::getURIResults() const +{ + return _uriResults; +} + } // namespace aria2 diff --git a/src/RequestGroup.h b/src/RequestGroup.h index e9d6558c..cd5dec9a 100644 --- a/src/RequestGroup.h +++ b/src/RequestGroup.h @@ -44,6 +44,8 @@ #include "TransferStat.h" #include "TimeA2.h" #include "Request.h" +#include "DownloadResult.h" +#include "URIResult.h" namespace aria2 { @@ -112,6 +114,10 @@ private: bool _forceHaltRequested; + // URIResult is stored in the ascending order of the time when its result is + // available. + std::deque _uriResults; + bool _singleHostMultiConnectionEnabled; std::deque > _preDownloadHandlers; @@ -154,6 +160,12 @@ private: bool tryAutoFileRenaming(); + // Returns the result code of this RequestGroup. + // If the download finished, then returns DownloadResult::FINISHED. + // If the download didn't finish and error result is available in _uriResults, + // then last result code is returned. + // Otherwise returns DownloadResult::UNKNOWN_ERROR. + DownloadResult::RESULT downloadResult() const; public: RequestGroup(const Option* option, const std::deque& uris); @@ -311,6 +323,10 @@ public: return _forceHaltRequested; } + void addURIResult(std::string uri, DownloadResult::RESULT result); + + const std::deque& getURIResults() const; + void dependsOn(const SharedHandle& dep); bool isDependencyResolved(); diff --git a/src/RequestGroupMan.cc b/src/RequestGroupMan.cc index e64a07aa..e2ec7379 100644 --- a/src/RequestGroupMan.cc +++ b/src/RequestGroupMan.cc @@ -377,16 +377,17 @@ void RequestGroupMan::closeFile() RequestGroupMan::DownloadStat RequestGroupMan::getDownloadStat() const { - DownloadStat stat; size_t finished = 0; size_t error = 0; size_t inprogress = 0; + DownloadResult::RESULT lastError = DownloadResult::FINISHED; for(std::deque >::const_iterator itr = _downloadResults.begin(); itr != _downloadResults.end(); ++itr) { if((*itr)->result == DownloadResult::FINISHED) { ++finished; } else { ++error; + lastError = (*itr)->result; } } for(RequestGroups::const_iterator itr = _requestGroups.begin(); @@ -398,11 +399,8 @@ RequestGroupMan::DownloadStat RequestGroupMan::getDownloadStat() const ++inprogress; } } - stat.setCompleted(finished); - stat.setError(error); - stat.setInProgress(inprogress); - stat.setWaiting(_reservedGroups.size()); - return stat; + return DownloadStat(finished, error, inprogress, _reservedGroups.size(), + lastError); } void RequestGroupMan::showDownloadResults(std::ostream& o) const diff --git a/src/RequestGroupMan.h b/src/RequestGroupMan.h index f793c4ec..3f7f5872 100644 --- a/src/RequestGroupMan.h +++ b/src/RequestGroupMan.h @@ -36,12 +36,15 @@ #define _D_REQUEST_GROUP_MAN_H_ #include "common.h" -#include "SharedHandle.h" -#include "TransferStat.h" + #include #include #include +#include "SharedHandle.h" +#include "DownloadResult.h" +#include "TransferStat.h" + namespace aria2 { class DownloadEngine; @@ -117,18 +120,34 @@ public: size_t _error; size_t _inProgress; size_t _waiting; + DownloadResult::RESULT _lastErrorResult; public: - DownloadStat():_completed(0), _error(0), _inProgress(0), _waiting(0) {} + DownloadStat(size_t completed, + size_t error, + size_t inProgress, + size_t waiting, + DownloadResult::RESULT lastErrorResult = + DownloadResult::FINISHED): + _completed(completed), + _error(error), + _inProgress(inProgress), + _waiting(waiting), + _lastErrorResult(lastErrorResult) {} - void setCompleted(size_t c) { _completed = c; } - void setError(size_t c) { _error = c; } - void setInProgress(size_t c) { _inProgress = c; } - void setWaiting(size_t c) { _waiting = c; } + DownloadResult::RESULT getLastErrorResult() const + { + return _lastErrorResult; + } bool allCompleted() const { return _error == 0 && _inProgress == 0 && _waiting == 0; } + + size_t getInProgress() const + { + return _inProgress; + } }; DownloadStat getDownloadStat() const; diff --git a/src/URIResult.cc b/src/URIResult.cc new file mode 100644 index 00000000..94992dfd --- /dev/null +++ b/src/URIResult.cc @@ -0,0 +1,52 @@ +/* */ +#include "URIResult.h" + +namespace aria2 { + +URIResult::URIResult(const std::string& uri, DownloadResult::RESULT result): + _uri(uri), _result(result) {} + +const std::string& URIResult::getURI() const +{ + return _uri; +} + +DownloadResult::RESULT URIResult::getResult() const +{ + return _result; +} + +} // namespace aria2 diff --git a/src/URIResult.h b/src/URIResult.h new file mode 100644 index 00000000..7a91e303 --- /dev/null +++ b/src/URIResult.h @@ -0,0 +1,62 @@ +/* */ +#ifndef _D_URI_RESULT_H_ +#define _D_URI_RESULT_H_ + +#include "common.h" + +#include + +#include "DownloadResult.h" + +namespace aria2 { + +// This class stores URI and its result code as a consequence of the download. +class URIResult { +private: + std::string _uri; + + DownloadResult::RESULT _result; +public: + URIResult(const std::string& uri, DownloadResult::RESULT result); + + const std::string& getURI() const; + + DownloadResult::RESULT getResult() const; +}; + +} // namespace aria2 + +#endif // _D_URI_RESULT_H_ diff --git a/src/main.cc b/src/main.cc index 94ee950e..d846d9c4 100644 --- a/src/main.cc +++ b/src/main.cc @@ -157,7 +157,7 @@ static void showFiles(const std::deque& uris, const Option* op) extern Option* option_processing(int argc, char* const argv[]); -int main(int argc, char* argv[]) +DownloadResult::RESULT main(int argc, char* argv[]) { Option* op = option_processing(argc, argv); std::deque args(argv+optind, argv+argc); @@ -175,7 +175,7 @@ int main(int argc, char* argv[]) if(op->getAsBool(PREF_QUIET)) { LogFactory::setConsoleOutput(false); } - int32_t exitStatus = EXIT_SUCCESS; + DownloadResult::RESULT exitStatus = DownloadResult::FINISHED; try { Logger* logger = LogFactory::getInstance(); logger->info("<<--- --- --- ---"); @@ -191,13 +191,12 @@ int main(int argc, char* argv[]) #ifdef SIGPIPE Util::setGlobalSignalHandler(SIGPIPE, SIG_IGN, 0); #endif - int32_t returnValue = 0; std::deque > requestGroups; #ifdef ENABLE_BITTORRENT if(!op->blank(PREF_TORRENT_FILE)) { if(op->get(PREF_SHOW_FILES) == V_TRUE) { showTorrentFile(op->get(PREF_TORRENT_FILE)); - return EXIT_SUCCESS; + return exitStatus; } else { createRequestGroupForBitTorrent(requestGroups, op, args); } @@ -208,7 +207,7 @@ int main(int argc, char* argv[]) if(!op->blank(PREF_METALINK_FILE)) { if(op->get(PREF_SHOW_FILES) == V_TRUE) { showMetalinkFile(op->get(PREF_METALINK_FILE), op); - return EXIT_SUCCESS; + return exitStatus; } else { createRequestGroupForMetalink(requestGroups, op); } @@ -228,15 +227,12 @@ int main(int argc, char* argv[]) if(requestGroups.empty()) { std::cout << MSG_NO_FILES_TO_DOWNLOAD << std::endl; } else { - returnValue = MultiUrlRequestInfo(requestGroups, op, getStatCalc(op), - getSummaryOut(op)).execute(); - } - if(returnValue == 1) { - exitStatus = EXIT_FAILURE; + exitStatus = MultiUrlRequestInfo(requestGroups, op, getStatCalc(op), + getSummaryOut(op)).execute(); } } catch(Exception& ex) { std::cerr << EX_EXCEPTION_CAUGHT << "\n" << ex.stackTrace() << std::endl; - exitStatus = EXIT_FAILURE; + exitStatus = DownloadResult::UNKNOWN_ERROR; } delete op; LogFactory::release(); @@ -248,7 +244,7 @@ int main(int argc, char* argv[]) int main(int argc, char* argv[]) { aria2::Platform platform; - int r = aria2::main(argc, argv); + aria2::DownloadResult::RESULT r = aria2::main(argc, argv); return r; } diff --git a/src/option_processing.cc b/src/option_processing.cc index 035cf1db..bc8d33d4 100644 --- a/src/option_processing.cc +++ b/src/option_processing.cc @@ -53,6 +53,7 @@ #include "File.h" #include "StringFormat.h" #include "OptionHandlerException.h" +#include "DownloadResult.h" extern char* optarg; extern int optind, opterr, optopt; @@ -107,7 +108,7 @@ Option* option_processing(int argc, char* const argv[]) oparser.parseDefaultValues(op); } catch(Exception& e) { std::cerr << e.stackTrace(); - exit(EXIT_FAILURE); + exit(DownloadResult::UNKNOWN_ERROR); } while(1) { @@ -254,12 +255,12 @@ Option* option_processing(int argc, char* const argv[]) std::cout << "--http-proxy-user was deprecated. See --http-proxy," << " --https-proxy, --ftp-proxy, --all-proxy options." << std::endl; - exit(EXIT_FAILURE); + exit(DownloadResult::UNKNOWN_ERROR); case 5: std::cout << "--http-proxy-passwd was deprecated. See --http-proxy," << " --https-proxy, --ftp-proxy, --all-proxy options." << std::endl; - exit(EXIT_FAILURE); + exit(DownloadResult::UNKNOWN_ERROR); case 6: cmdstream << PREF_HTTP_AUTH_SCHEME << "=" << optarg << "\n"; break; @@ -282,12 +283,12 @@ Option* option_processing(int argc, char* const argv[]) std::cout << "--ftp-via-http-proxy was deprecated." << " Use --http-proxy-method option instead." << std::endl; - exit(EXIT_FAILURE); + exit(DownloadResult::UNKNOWN_ERROR); case 14: std::cout << "--http-proxy-method was deprecated." << " Use --proxy-method option instead." << std::endl; - exit(EXIT_FAILURE); + exit(DownloadResult::UNKNOWN_ERROR); case 15: cmdstream << PREF_LISTEN_PORT << "=" << optarg << "\n"; break; @@ -553,7 +554,7 @@ Option* option_processing(int argc, char* const argv[]) break; case 'v': showVersion(); - exit(EXIT_SUCCESS); + exit(DownloadResult::FINISHED); case 'h': { std::string category; @@ -563,11 +564,11 @@ Option* option_processing(int argc, char* const argv[]) category = optarg; } showUsage(category, oparser); - exit(EXIT_SUCCESS); + exit(DownloadResult::FINISHED); } default: showUsage(TAG_HELP, oparser); - exit(EXIT_FAILURE); + exit(DownloadResult::UNKNOWN_ERROR); } } @@ -588,18 +589,18 @@ Option* option_processing(int argc, char* const argv[]) << "Usage:" << "\n" << oparser.findByName(e.getOptionName())->getDescription() << std::endl; - exit(EXIT_FAILURE); + exit(DownloadResult::UNKNOWN_ERROR); } catch(Exception& e) { std::cerr << "Parse error in " << cfname << "\n" << e.stackTrace() << std::endl; - exit(EXIT_FAILURE); + exit(DownloadResult::UNKNOWN_ERROR); } } else if(!ucfname.empty()) { std::cerr << StringFormat("Configuration file %s is not found.", cfname.c_str()) << "\n"; showUsage(TAG_HELP, oparser); - exit(EXIT_FAILURE); + exit(DownloadResult::UNKNOWN_ERROR); } } // Override configuration with environment variables. @@ -616,11 +617,11 @@ Option* option_processing(int argc, char* const argv[]) << "Usage:" << "\n" << oparser.findByName(e.getOptionName())->getDescription() << std::endl; - exit(EXIT_FAILURE); + exit(DownloadResult::UNKNOWN_ERROR); } catch(Exception& e) { std::cerr << e.stackTrace() << std::endl; showUsage(TAG_HELP, oparser); - exit(EXIT_FAILURE); + exit(DownloadResult::UNKNOWN_ERROR); } } if( @@ -634,14 +635,14 @@ Option* option_processing(int argc, char* const argv[]) if(optind == argc) { std::cerr << MSG_URI_REQUIRED << std::endl; showUsage(TAG_HELP, oparser); - exit(EXIT_FAILURE); + exit(DownloadResult::UNKNOWN_ERROR); } } #ifdef HAVE_DAEMON if(op->getAsBool(PREF_DAEMON)) { if(daemon(1, 1) < 0) { perror(MSG_DAEMON_FAILED); - exit(EXIT_FAILURE); + exit(DownloadResult::UNKNOWN_ERROR); } } #endif // HAVE_DAEMON diff --git a/test/RequestGroupTest.cc b/test/RequestGroupTest.cc index da14ef4a..4f6589fa 100644 --- a/test/RequestGroupTest.cc +++ b/test/RequestGroupTest.cc @@ -6,6 +6,7 @@ #include "Option.h" #include "SingleFileDownloadContext.h" #include "FileEntry.h" +#include "PieceStorage.h" namespace aria2 { @@ -15,6 +16,7 @@ class RequestGroupTest : public CppUnit::TestFixture { CPPUNIT_TEST(testRegisterSearchRemove); CPPUNIT_TEST(testRemoveURIWhoseHostnameIs); CPPUNIT_TEST(testGetFilePath); + CPPUNIT_TEST(testCreateDownloadResult); CPPUNIT_TEST_SUITE_END(); private: @@ -24,6 +26,7 @@ public: void testRegisterSearchRemove(); void testRemoveURIWhoseHostnameIs(); void testGetFilePath(); + void testCreateDownloadResult(); }; @@ -93,4 +96,47 @@ void RequestGroupTest::testGetFilePath() CPPUNIT_ASSERT_EQUAL(std::string("[MEMORY]myfile"), group.getFilePath()); } +void RequestGroupTest::testCreateDownloadResult() +{ + SharedHandle ctx + (new SingleFileDownloadContext(1024, 1024*1024, "myfile")); + ctx->setDir("/tmp"); + Option op; + std::deque uris; + uris.push_back("http://first/file"); + uris.push_back("http://second/file"); + + RequestGroup group(&op, uris); + group.setDownloadContext(ctx); + group.initPieceStorage(); + { + SharedHandle result = group.createDownloadResult(); + + CPPUNIT_ASSERT_EQUAL(std::string("/tmp/myfile"), result->filePath); + CPPUNIT_ASSERT_EQUAL((uint64_t)1024*1024, result->totalLength); + CPPUNIT_ASSERT_EQUAL(std::string("http://first/file"), result->uri); + CPPUNIT_ASSERT_EQUAL((size_t)2, result->numUri); + CPPUNIT_ASSERT_EQUAL((uint64_t)0, result->sessionDownloadLength); + CPPUNIT_ASSERT_EQUAL((time_t)0, result->sessionTime); + // result is UNKNOWN_ERROR if download has not completed and no specific + // error has been reported + CPPUNIT_ASSERT_EQUAL(DownloadResult::UNKNOWN_ERROR, result->result); + } + { + group.addURIResult("http://first/file", DownloadResult::TIME_OUT); + group.addURIResult("http://second/file",DownloadResult::RESOURCE_NOT_FOUND); + + SharedHandle result = group.createDownloadResult(); + + CPPUNIT_ASSERT_EQUAL(DownloadResult::RESOURCE_NOT_FOUND, result->result); + } + { + group.getPieceStorage()->markAllPiecesDone(); + + SharedHandle result = group.createDownloadResult(); + + CPPUNIT_ASSERT_EQUAL(DownloadResult::FINISHED, result->result); + } +} + } // namespace aria2