Procházet zdrojové kódy

Move initialization code in aria2::main to struct Context

Tatsuhiro Tsujikawa před 12 roky
rodič
revize
90abec8a36

+ 298 - 0
src/Context.cc

@@ -0,0 +1,298 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2013 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "Context.h"
+
+#include <signal.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include <numeric>
+#include <vector>
+#include <iostream>
+
+#include "SharedHandle.h"
+#include "LogFactory.h"
+#include "Logger.h"
+#include "util.h"
+#include "FeatureConfig.h"
+#include "MultiUrlRequestInfo.h"
+#include "SimpleRandomizer.h"
+#include "File.h"
+#include "message.h"
+#include "prefs.h"
+#include "Option.h"
+#include "a2algo.h"
+#include "a2io.h"
+#include "a2time.h"
+#include "Platform.h"
+#include "FileEntry.h"
+#include "RequestGroup.h"
+#include "ConsoleStatCalc.h"
+#include "NullStatCalc.h"
+#include "download_helper.h"
+#include "Exception.h"
+#include "ProtocolDetector.h"
+#include "RecoverableException.h"
+#include "SocketCore.h"
+#include "DownloadContext.h"
+#include "fmt.h"
+#include "NullOutputFile.h"
+#include "console.h"
+#include "UriListParser.h"
+#ifdef ENABLE_BITTORRENT
+# include "bittorrent_helper.h"
+#endif // ENABLE_BITTORRENT
+#ifdef ENABLE_METALINK
+# include "metalink_helper.h"
+# include "MetalinkEntry.h"
+#endif // ENABLE_METALINK
+#ifdef ENABLE_MESSAGE_DIGEST
+# include "message_digest_helper.h"
+#endif // ENABLE_MESSAGE_DIGEST
+
+extern char* optarg;
+extern int optind, opterr, optopt;
+
+namespace aria2 {
+
+SharedHandle<StatCalc> getStatCalc(const SharedHandle<Option>& op)
+{
+  SharedHandle<StatCalc> statCalc;
+  if(op->getAsBool(PREF_QUIET)) {
+    statCalc.reset(new NullStatCalc());
+  } else {
+    SharedHandle<ConsoleStatCalc> impl
+      (new ConsoleStatCalc(op->getAsInt(PREF_SUMMARY_INTERVAL),
+                           op->getAsBool(PREF_HUMAN_READABLE)));
+    impl->setReadoutVisibility(op->getAsBool(PREF_SHOW_CONSOLE_READOUT));
+    impl->setTruncate(op->getAsBool(PREF_TRUNCATE_CONSOLE_READOUT));
+    statCalc = impl;
+  }
+  return statCalc;
+}
+
+SharedHandle<OutputFile> getSummaryOut(const SharedHandle<Option>& op)
+{
+  if(op->getAsBool(PREF_QUIET)) {
+    return SharedHandle<OutputFile>(new NullOutputFile());
+  } else {
+    return global::cout();
+  }
+}
+
+#ifdef ENABLE_BITTORRENT
+namespace {
+void showTorrentFile(const std::string& uri)
+{
+  SharedHandle<Option> op(new Option());
+  SharedHandle<DownloadContext> dctx(new DownloadContext());
+  bittorrent::load(uri, dctx, op);
+  bittorrent::print(*global::cout(), dctx);
+}
+} // namespace
+#endif // ENABLE_BITTORRENT
+
+#ifdef ENABLE_METALINK
+namespace {
+void showMetalinkFile
+(const std::string& uri, const SharedHandle<Option>& op)
+{
+  std::vector<SharedHandle<MetalinkEntry> > metalinkEntries;
+  metalink::parseAndQuery(metalinkEntries, uri, op.get(),
+                          op->get(PREF_METALINK_BASE_URI));
+  std::vector<SharedHandle<FileEntry> > fileEntries;
+  MetalinkEntry::toFileEntry(fileEntries, metalinkEntries);
+  util::toStream(fileEntries.begin(), fileEntries.end(), *global::cout());
+  global::cout()->write("\n");
+  global::cout()->flush();
+}
+} // namespace
+#endif // ENABLE_METALINK
+
+#if defined ENABLE_BITTORRENT || defined ENABLE_METALINK
+namespace {
+void showFiles
+(const std::vector<std::string>& uris, const SharedHandle<Option>& op)
+{
+  ProtocolDetector dt;
+  for(std::vector<std::string>::const_iterator i = uris.begin(),
+        eoi = uris.end(); i != eoi; ++i) {
+    printf(">>> ");
+    printf(MSG_SHOW_FILES, (*i).c_str());
+    printf("\n");
+    try {
+#ifdef ENABLE_BITTORRENT
+      if(dt.guessTorrentFile(*i)) {
+        showTorrentFile(*i);
+      } else
+#endif // ENABLE_BITTORRENT
+#ifdef ENABLE_METALINK
+        if(dt.guessMetalinkFile(*i)) {
+          showMetalinkFile(*i, op);
+        } else
+#endif // ENABLE_METALINK
+          {
+            printf(MSG_NOT_TORRENT_METALINK);
+            printf("\n\n");
+          }
+    } catch(RecoverableException& e) {
+      global::cout()->printf("%s\n", e.stackTrace().c_str());
+    }
+  }
+}
+} // namespace
+#endif // ENABLE_BITTORRENT || ENABLE_METALINK
+
+extern void option_processing(Option& option, std::vector<std::string>& uris,
+                              int argc, char* argv[]);
+
+Context::Context(int argc, char** argv)
+{
+  std::vector<std::string> args;
+  SharedHandle<Option> op(new Option());
+  option_processing(*op.get(), args, argc, argv);
+
+  SimpleRandomizer::init();
+#ifdef ENABLE_BITTORRENT
+  bittorrent::generateStaticPeerId(op->get(PREF_PEER_ID_PREFIX));
+#endif // ENABLE_BITTORRENT
+  LogFactory::setLogFile(op->get(PREF_LOG));
+  LogFactory::setLogLevel(op->get(PREF_LOG_LEVEL));
+  LogFactory::setConsoleLogLevel(op->get(PREF_CONSOLE_LOG_LEVEL));
+  if(op->getAsBool(PREF_QUIET)) {
+    LogFactory::setConsoleOutput(false);
+  }
+  LogFactory::reconfigure();
+  A2_LOG_INFO("<<--- --- --- ---");
+  A2_LOG_INFO("  --- --- --- ---");
+  A2_LOG_INFO("  --- --- --- --->>");
+  A2_LOG_INFO(fmt("%s %s %s", PACKAGE, PACKAGE_VERSION, TARGET));
+  A2_LOG_INFO(MSG_LOGGING_STARTED);
+
+  if(op->getAsBool(PREF_DISABLE_IPV6)) {
+    SocketCore::setProtocolFamily(AF_INET);
+    // Get rid of AI_ADDRCONFIG. It causes name resolution error
+    // when none of network interface has IPv4 address.
+    setDefaultAIFlags(0);
+  }
+  net::checkAddrconfig();
+  // Bind interface
+  if(!op->get(PREF_INTERFACE).empty()) {
+    std::string iface = op->get(PREF_INTERFACE);
+    SocketCore::bindAddress(iface);
+  }
+  sigset_t mask;
+#ifdef HAVE_SIGACTION
+  sigemptyset(&mask);
+#else // !HAVE_SIGACTION
+  mask = 0;
+#endif // !HAVE_SIGACTION
+#ifdef SIGPIPE
+  util::setGlobalSignalHandler(SIGPIPE, &mask, SIG_IGN, 0);
+#endif
+#ifdef SIGCHLD
+  // Avoid to create zombie process when forked child processes are
+  // died.
+  util::setGlobalSignalHandler(SIGCHLD, &mask, SIG_IGN, 0);
+#endif // SIGCHILD
+  std::vector<SharedHandle<RequestGroup> > requestGroups;
+  SharedHandle<UriListParser> uriListParser;
+#ifdef ENABLE_BITTORRENT
+  if(!op->blank(PREF_TORRENT_FILE)) {
+    if(op->get(PREF_SHOW_FILES) == A2_V_TRUE) {
+      showTorrentFile(op->get(PREF_TORRENT_FILE));
+      return;
+    } else {
+      createRequestGroupForBitTorrent(requestGroups, op, args,
+                                      op->get(PREF_TORRENT_FILE));
+    }
+  }
+  else
+#endif // ENABLE_BITTORRENT
+#ifdef ENABLE_METALINK
+    if(!op->blank(PREF_METALINK_FILE)) {
+      if(op->get(PREF_SHOW_FILES) == A2_V_TRUE) {
+        showMetalinkFile(op->get(PREF_METALINK_FILE), op);
+        return;
+      } else {
+        createRequestGroupForMetalink(requestGroups, op);
+      }
+    }
+    else
+#endif // ENABLE_METALINK
+      if(!op->blank(PREF_INPUT_FILE)) {
+        if(op->getAsBool(PREF_DEFERRED_INPUT)) {
+          uriListParser = openUriListParser(op->get(PREF_INPUT_FILE));
+        } else {
+          createRequestGroupForUriList(requestGroups, op);
+        }
+#if defined ENABLE_BITTORRENT || defined ENABLE_METALINK
+      } else if(op->get(PREF_SHOW_FILES) == A2_V_TRUE) {
+        showFiles(args, op);
+        return;
+#endif // ENABLE_METALINK || ENABLE_METALINK
+      } else {
+        createRequestGroupForUri(requestGroups, op, args, false, false, true);
+      }
+
+  // Remove option values which is only valid for URIs specified in
+  // command-line. If they are left, because op is used as a template
+  // for new RequestGroup(such as created in RPC command), they causes
+  // unintentional effect.
+  for(SharedHandle<Option> i = op; i; i = i->getParent()) {
+    i->remove(PREF_OUT);
+    i->remove(PREF_FORCE_SEQUENTIAL);
+    i->remove(PREF_INPUT_FILE);
+    i->remove(PREF_INDEX_OUT);
+    i->remove(PREF_SELECT_FILE);
+    i->remove(PREF_PAUSE);
+    i->remove(PREF_CHECKSUM);
+    i->remove(PREF_GID);
+  }
+  if(!op->getAsBool(PREF_ENABLE_RPC) && requestGroups.empty() &&
+     !uriListParser) {
+    global::cout()->printf("%s\n", MSG_NO_FILES_TO_DOWNLOAD);
+  } else {
+    reqinfo.reset(new MultiUrlRequestInfo(requestGroups, op, getStatCalc(op),
+                                          getSummaryOut(op),
+                                          uriListParser));
+  }
+}
+
+Context::~Context()
+{
+}
+
+} // namespace aria2

+ 53 - 0
src/Context.h

@@ -0,0 +1,53 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2013 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef CONTEXT_H
+#define CONTEXT_H
+
+#include "common.h"
+#include "SharedHandle.h"
+
+namespace aria2 {
+
+class MultiUrlRequestInfo;
+
+struct Context {
+  Context(int argc, char** argv);
+  ~Context();
+  SharedHandle<MultiUrlRequestInfo> reqinfo;
+};
+
+} // namespace aria2
+
+#endif // CONTEXT_H

+ 12 - 6
src/DownloadEngine.cc

@@ -86,7 +86,7 @@ volatile sig_atomic_t globalHaltRequested = 0;
 DownloadEngine::DownloadEngine(const SharedHandle<EventPoll>& eventPoll)
   : eventPoll_(eventPoll),
     haltRequested_(0),
-    noWait_(false),
+    noWait_(true),
     refreshInterval_(DEFAULT_REFRESH_INTERVAL),
     cookieStorage_(new CookieStorage()),
 #ifdef ENABLE_BITTORRENT
@@ -139,11 +139,15 @@ void executeCommand(std::deque<Command*>& commands,
 }
 } // namespace
 
-void DownloadEngine::run()
+int DownloadEngine::run(bool oneshot)
 {
   Timer cp;
   cp.reset(0);
   while(!commands_.empty() || !routineCommands_.empty()) {
+    if(!commands_.empty()) {
+      waitData();
+    }
+    noWait_ = false;
     global::wallclock().reset();
     calculateStatistics();
     if(cp.differenceInMillis(global::wallclock())+A2_DELTA_MILLIS >=
@@ -156,12 +160,14 @@ void DownloadEngine::run()
     }
     executeCommand(routineCommands_, Command::STATUS_ALL);
     afterEachIteration();
-    if(!commands_.empty()) {
-      waitData();
+    if(oneshot) {
+      return 1;
     }
-    noWait_ = false;
   }
-  onEndOfRun();
+  if(!oneshot) {
+    onEndOfRun();
+  }
+  return 0;
 }
 
 void DownloadEngine::waitData()

+ 5 - 1
src/DownloadEngine.h

@@ -167,7 +167,11 @@ public:
 
   ~DownloadEngine();
 
-  void run();
+  // If oneshot is true, this function returns after one event polling
+  // and performing action for them. This function returns 1 when
+  // oneshot is true and there are still downloads to be
+  // processed. Otherwise, returns 0.
+  int run(bool oneshot=false);
 
   void cleanQueue();
 

+ 3 - 2
src/Makefile.am

@@ -1,9 +1,10 @@
 SUBDIRS = includes
 bin_PROGRAMS = aria2c
 aria2c_SOURCES = main.cc\
-	option_processing.cc\
 	version_usage.cc
-SRCS =  SocketCore.cc SocketCore.h\
+SRCS =  option_processing.cc\
+	Context.cc Context.h\
+	SocketCore.cc SocketCore.h\
 	BinaryStream.h\
 	Command.cc Command.h\
 	AbstractCommand.cc AbstractCommand.h\

+ 102 - 67
src/MultiUrlRequestInfo.cc

@@ -109,12 +109,14 @@ MultiUrlRequestInfo::MultiUrlRequestInfo
  const SharedHandle<StatCalc>& statCalc,
  const SharedHandle<OutputFile>& summaryOut,
  const SharedHandle<UriListParser>& uriListParser)
-  : requestGroups_(requestGroups),
-    option_(op),
+  : option_(op),
     statCalc_(statCalc),
     summaryOut_(summaryOut),
     uriListParser_(uriListParser)
-{}
+    // TODO init mask_
+{
+  requestGroups_.swap(requestGroups);
+}
 
 MultiUrlRequestInfo::~MultiUrlRequestInfo() {}
 
@@ -126,17 +128,15 @@ void MultiUrlRequestInfo::printMessageForContinue()
      _("If there are any errors, then see the log file. See '-l' option in help/man page for details."));
 }
 
-error_code::Value MultiUrlRequestInfo::execute()
+error_code::Value MultiUrlRequestInfo::prepare()
 {
-  error_code::Value returnValue = error_code::FINISHED;
-  sigset_t mask;
   try {
     SharedHandle<rpc::WebSocketSessionMan> wsSessionMan;
     if(option_->getAsBool(PREF_ENABLE_RPC)) {
       wsSessionMan.reset(new rpc::WebSocketSessionMan());
     }
-    Notifier notifier(wsSessionMan);
-    SingletonHolder<Notifier>::instance(&notifier);
+    SharedHandle<Notifier> notifier(new Notifier(wsSessionMan));
+    SingletonHolder<Notifier>::instance(notifier);
 
 #ifdef ENABLE_SSL
     if(option_->getAsBool(PREF_ENABLE_RPC) &&
@@ -159,15 +159,16 @@ error_code::Value MultiUrlRequestInfo::execute()
     }
 #endif // ENABLE_SSL
 
-    SharedHandle<DownloadEngine> e =
-      DownloadEngineFactory().newDownloadEngine(option_.get(), requestGroups_);
+    e_ = DownloadEngineFactory().newDownloadEngine(option_.get(),
+                                                   requestGroups_);
     // Avoid keeping RequestGroups alive longer than necessary
     requestGroups_.clear();
 
     if(!option_->blank(PREF_LOAD_COOKIES)) {
       File cookieFile(option_->get(PREF_LOAD_COOKIES));
       if(cookieFile.isFile() &&
-         e->getCookieStorage()->load(cookieFile.getPath(), Time().getTime())) {
+         e_->getCookieStorage()->load(cookieFile.getPath(),
+                                      Time().getTime())) {
         A2_LOG_INFO(fmt("Loaded cookies from '%s'.",
                         cookieFile.getPath().c_str()));
       } else {
@@ -194,7 +195,7 @@ error_code::Value MultiUrlRequestInfo::execute()
         authConfigFactory->setNetrc(netrc);
       }
     }
-    e->setAuthConfigFactory(authConfigFactory);
+    e_->setAuthConfigFactory(authConfigFactory);
 
 #ifdef ENABLE_SSL
     SharedHandle<TLSContext> clTlsContext(TLSContext::make(TLS_CLIENT));
@@ -220,7 +221,7 @@ error_code::Value MultiUrlRequestInfo::execute()
 #ifdef HAVE_ARES_ADDR_NODE
     ares_addr_node* asyncDNSServers =
       parseAsyncDNSServers(option_->get(PREF_ASYNC_DNS_SERVER));
-    e->setAsyncDNSServers(asyncDNSServers);
+    e_->setAsyncDNSServers(asyncDNSServers);
 #endif // HAVE_ARES_ADDR_NODE
     if(!Timer::monotonicClock()) {
       A2_LOG_WARN("Don't change system time while aria2c is running."
@@ -229,86 +230,120 @@ error_code::Value MultiUrlRequestInfo::execute()
 
     std::string serverStatIf = option_->get(PREF_SERVER_STAT_IF);
     if(!serverStatIf.empty()) {
-      e->getRequestGroupMan()->loadServerStat(serverStatIf);
-      e->getRequestGroupMan()->removeStaleServerStat
+      e_->getRequestGroupMan()->loadServerStat(serverStatIf);
+      e_->getRequestGroupMan()->removeStaleServerStat
         (option_->getAsInt(PREF_SERVER_STAT_TIMEOUT));
     }
-    e->setStatCalc(statCalc_);
+    e_->setStatCalc(statCalc_);
     if(uriListParser_) {
-      e->getRequestGroupMan()->setUriListParser(uriListParser_);
+      e_->getRequestGroupMan()->setUriListParser(uriListParser_);
     }
 #ifdef HAVE_SIGACTION
-    sigemptyset(&mask);
-    sigaddset(&mask, SIGINT);
-    sigaddset(&mask, SIGTERM);
+    sigemptyset(&mask_);
+    sigaddset(&mask_, SIGINT);
+    sigaddset(&mask_, SIGTERM);
 #ifdef SIGHUP
-    sigaddset(&mask, SIGHUP);
+    sigaddset(&mask_, SIGHUP);
 #endif // SIGHUP
 #else // !HAVE_SIGACTION
-    mask = 0;
+    mask_ = 0;
 #endif // !HAVE_SIGACTION
 
 #ifdef SIGHUP
-    util::setGlobalSignalHandler(SIGHUP, &mask, handler, 0);
+    util::setGlobalSignalHandler(SIGHUP, &mask_, handler, 0);
 #endif // SIGHUP
-    util::setGlobalSignalHandler(SIGINT, &mask, handler, 0);
-    util::setGlobalSignalHandler(SIGTERM, &mask, handler, 0);
+    util::setGlobalSignalHandler(SIGINT, &mask_, handler, 0);
+    util::setGlobalSignalHandler(SIGTERM, &mask_, handler, 0);
 
-    e->getRequestGroupMan()->getNetStat().downloadStart();
-    e->run();
+    e_->getRequestGroupMan()->getNetStat().downloadStart();
+  } catch(RecoverableException& e) {
+    SingletonHolder<Notifier>::clear();
+    resetSignalHandlers();
+    return error_code::UNKNOWN_ERROR;
+  }
+  return error_code::FINISHED;
+}
 
-    if(!option_->blank(PREF_SAVE_COOKIES)) {
-      e->getCookieStorage()->saveNsFormat(option_->get(PREF_SAVE_COOKIES));
-    }
+error_code::Value MultiUrlRequestInfo::getResult()
+{
+  error_code::Value returnValue = error_code::FINISHED;
+  if(!option_->blank(PREF_SAVE_COOKIES)) {
+    e_->getCookieStorage()->saveNsFormat(option_->get(PREF_SAVE_COOKIES));
+  }
 
-    const std::string& serverStatOf = option_->get(PREF_SERVER_STAT_OF);
-    if(!serverStatOf.empty()) {
-      e->getRequestGroupMan()->saveServerStat(serverStatOf);
-    }
-    e->getRequestGroupMan()->showDownloadResults
-      (*summaryOut_, option_->get(PREF_DOWNLOAD_RESULT) == A2_V_FULL);
-    summaryOut_->flush();
+  const std::string& serverStatOf = option_->get(PREF_SERVER_STAT_OF);
+  if(!serverStatOf.empty()) {
+    e_->getRequestGroupMan()->saveServerStat(serverStatOf);
+  }
+  e_->getRequestGroupMan()->showDownloadResults
+    (*summaryOut_, option_->get(PREF_DOWNLOAD_RESULT) == A2_V_FULL);
+  summaryOut_->flush();
 
-    RequestGroupMan::DownloadStat s =
-      e->getRequestGroupMan()->getDownloadStat();
-    if(!s.allCompleted()) {
-      printMessageForContinue();
-      if(s.getLastErrorResult() == error_code::FINISHED &&
-         s.getInProgress() > 0) {
-        returnValue = error_code::IN_PROGRESS;
-      } else {
-        returnValue = s.getLastErrorResult();
-      }
+  RequestGroupMan::DownloadStat s =
+    e_->getRequestGroupMan()->getDownloadStat();
+  if(!s.allCompleted()) {
+    printMessageForContinue();
+    if(s.getLastErrorResult() == error_code::FINISHED &&
+       s.getInProgress() > 0) {
+      returnValue = error_code::IN_PROGRESS;
+    } else {
+      returnValue = s.getLastErrorResult();
     }
-    SessionSerializer sessionSerializer(e->getRequestGroupMan());
-    // TODO Add option: --save-session-status=error,inprogress,waiting
-    if(!option_->blank(PREF_SAVE_SESSION)) {
-      const std::string& filename = option_->get(PREF_SAVE_SESSION);
-      if(sessionSerializer.save(filename)) {
-        A2_LOG_NOTICE(fmt(_("Serialized session to '%s' successfully."),
-                          filename.c_str()));
-      } else {
-        A2_LOG_NOTICE(fmt(_("Failed to serialize session to '%s'."),
-                          filename.c_str()));
-      }
+  }
+  SessionSerializer sessionSerializer(e_->getRequestGroupMan());
+  // TODO Add option: --save-session-status=error,inprogress,waiting
+  if(!option_->blank(PREF_SAVE_SESSION)) {
+    const std::string& filename = option_->get(PREF_SAVE_SESSION);
+    if(sessionSerializer.save(filename)) {
+      A2_LOG_NOTICE(fmt(_("Serialized session to '%s' successfully."),
+                        filename.c_str()));
+    } else {
+      A2_LOG_NOTICE(fmt(_("Failed to serialize session to '%s'."),
+                        filename.c_str()));
     }
+  }
+  SingletonHolder<Notifier>::clear();
+  return returnValue;
+}
+
+int MultiUrlRequestInfo::run()
+{
+  int rv;
+  try {
+    rv = e_->run(true);
+  } catch(RecoverableException& e) {
+    rv = -1;
+  }
+  return rv;
+}
+
+error_code::Value MultiUrlRequestInfo::execute()
+{
+  error_code::Value returnValue;
+  returnValue = prepare();
+  if(returnValue != error_code::FINISHED) {
+    return returnValue;
+  }
+  try {
+    e_->run();
   } catch(RecoverableException& e) {
-    if(returnValue == error_code::FINISHED) {
-      returnValue = error_code::UNKNOWN_ERROR;
-    }
     A2_LOG_ERROR_EX(EX_EXCEPTION_CAUGHT, e);
   }
-  SingletonHolder<Notifier>::instance(0);
+  returnValue = getResult();
+  resetSignalHandlers();
+  return returnValue;
+}
 
+void MultiUrlRequestInfo::resetSignalHandlers()
+{
 #ifdef HAVE_SIGACTION
-  sigemptyset(&mask);
+  sigemptyset(&mask_);
 #endif // HAVE_SIGACTION
 #ifdef SIGHUP
-  util::setGlobalSignalHandler(SIGHUP, &mask, SIG_DFL, 0);
+  util::setGlobalSignalHandler(SIGHUP, &mask_, SIG_DFL, 0);
 #endif // SIGHUP
-  util::setGlobalSignalHandler(SIGINT, &mask, SIG_DFL, 0);
-  util::setGlobalSignalHandler(SIGTERM, &mask, SIG_DFL, 0);
-  return returnValue;
+  util::setGlobalSignalHandler(SIGINT, &mask_, SIG_DFL, 0);
+  util::setGlobalSignalHandler(SIGTERM, &mask_, SIG_DFL, 0);
 }
 
 } // namespace aria2

+ 18 - 1
src/MultiUrlRequestInfo.h

@@ -37,10 +37,13 @@
 
 #include "common.h"
 
+#include <signal.h>
+
 #include <vector>
 
 #include "SharedHandle.h"
 #include "DownloadResult.h"
+#include "util.h"
 
 namespace aria2 {
 
@@ -49,10 +52,11 @@ class Option;
 class StatCalc;
 class OutputFile;
 class UriListParser;
+class DownloadEngine;
 
 class MultiUrlRequestInfo {
 private:
-  std::vector<SharedHandle<RequestGroup> >& requestGroups_;
+  std::vector<SharedHandle<RequestGroup> > requestGroups_;
 
   SharedHandle<Option> option_;
 
@@ -62,7 +66,12 @@ private:
 
   SharedHandle<UriListParser> uriListParser_;
 
+  SharedHandle<DownloadEngine> e_;
+
+  sigset_t mask_;
+
   void printMessageForContinue();
+  void resetSignalHandlers();
 public:
   /*
    * MultiRequestInfo effectively takes ownership of the
@@ -82,6 +91,14 @@ public:
    * last download result.
    */
   error_code::Value execute();
+
+  error_code::Value prepare();
+  error_code::Value getResult();
+  // Returns 1 if the caller needs to call this function one or more
+  // time. Returns 0 if the function succeeds. Returns -1 on error.
+  // For return value 0 and -1, the caller must call tearDown() to get
+  // final error code.
+  int run();
 };
 
 } // namespace aria2

+ 2 - 3
src/RequestGroupMan.cc

@@ -214,9 +214,8 @@ void notifyDownloadEvent
 (const std::string& event, const SharedHandle<RequestGroup>& group)
 {
   // Check NULL to make unit test easier.
-  Notifier* notifier = SingletonHolder<Notifier>::instance();
-  if(notifier) {
-    notifier->notifyDownloadEvent(event, group);
+  if(SingletonHolder<Notifier>::instance()) {
+    SingletonHolder<Notifier>::instance()->notifyDownloadEvent(event, group);
   }
 }
 

+ 11 - 4
src/SingletonHolder.h

@@ -35,30 +35,37 @@
 #ifndef D_SINGLETON_HOLDER_H
 #define D_SINGLETON_HOLDER_H
 
+#include "SharedHandle.h"
+
 namespace aria2 {
 
 template<typename T>
 class SingletonHolder {
 private:
-  static T* instance_;
+  static SharedHandle<T> instance_;
 
   SingletonHolder() {}
 public:
   ~SingletonHolder() {}
 
-  static T* instance()
+  static const SharedHandle<T>& instance()
   {
     return instance_;
   }
 
-  static void instance(T* instance)
+  static void instance(const SharedHandle<T>& instance)
   {
     instance_ = instance;
   }
+
+  static void clear()
+  {
+    instance_.reset();
+  }
 };
 
 template<typename T>
-T* SingletonHolder<T>::instance_ = 0;
+SharedHandle<T> SingletonHolder<T>::instance_;
 
 } // namespace aria2
 

+ 6 - 243
src/main.cc

@@ -34,268 +34,31 @@
 /* copyright --> */
 #include "common.h"
 
-#include <signal.h>
 #include <unistd.h>
-#include <getopt.h>
-
-#include <numeric>
-#include <vector>
-#include <iostream>
 
 #include "SharedHandle.h"
-#include "LogFactory.h"
-#include "Logger.h"
-#include "util.h"
-#include "FeatureConfig.h"
+#include "Context.h"
 #include "MultiUrlRequestInfo.h"
-#include "SimpleRandomizer.h"
-#include "File.h"
 #include "message.h"
-#include "prefs.h"
-#include "Option.h"
-#include "a2algo.h"
-#include "a2io.h"
-#include "a2time.h"
 #include "Platform.h"
-#include "FileEntry.h"
-#include "RequestGroup.h"
-#include "ConsoleStatCalc.h"
-#include "NullStatCalc.h"
-#include "download_helper.h"
 #include "Exception.h"
-#include "ProtocolDetector.h"
-#include "RecoverableException.h"
-#include "SocketCore.h"
-#include "DownloadContext.h"
-#include "fmt.h"
-#include "NullOutputFile.h"
 #include "console.h"
-#include "UriListParser.h"
-#ifdef ENABLE_BITTORRENT
-# include "bittorrent_helper.h"
-#endif // ENABLE_BITTORRENT
-#ifdef ENABLE_METALINK
-# include "metalink_helper.h"
-# include "MetalinkEntry.h"
-#endif // ENABLE_METALINK
-#ifdef ENABLE_MESSAGE_DIGEST
-# include "message_digest_helper.h"
-#endif // ENABLE_MESSAGE_DIGEST
-
-extern char* optarg;
-extern int optind, opterr, optopt;
 
 namespace aria2 {
 
-SharedHandle<StatCalc> getStatCalc(const SharedHandle<Option>& op)
-{
-  SharedHandle<StatCalc> statCalc;
-  if(op->getAsBool(PREF_QUIET)) {
-    statCalc.reset(new NullStatCalc());
-  } else {
-    SharedHandle<ConsoleStatCalc> impl
-      (new ConsoleStatCalc(op->getAsInt(PREF_SUMMARY_INTERVAL),
-                           op->getAsBool(PREF_HUMAN_READABLE)));
-    impl->setReadoutVisibility(op->getAsBool(PREF_SHOW_CONSOLE_READOUT));
-    impl->setTruncate(op->getAsBool(PREF_TRUNCATE_CONSOLE_READOUT));
-    statCalc = impl;
-  }
-  return statCalc;
-}
-
-SharedHandle<OutputFile> getSummaryOut(const SharedHandle<Option>& op)
+error_code::Value main(int argc, char** argv)
 {
-  if(op->getAsBool(PREF_QUIET)) {
-    return SharedHandle<OutputFile>(new NullOutputFile());
-  } else {
-    return global::cout();
-  }
-}
-
-#ifdef ENABLE_BITTORRENT
-namespace {
-void showTorrentFile(const std::string& uri)
-{
-  SharedHandle<Option> op(new Option());
-  SharedHandle<DownloadContext> dctx(new DownloadContext());
-  bittorrent::load(uri, dctx, op);
-  bittorrent::print(*global::cout(), dctx);
-}
-} // namespace
-#endif // ENABLE_BITTORRENT
-
-#ifdef ENABLE_METALINK
-namespace {
-void showMetalinkFile
-(const std::string& uri, const SharedHandle<Option>& op)
-{
-  std::vector<SharedHandle<MetalinkEntry> > metalinkEntries;
-  metalink::parseAndQuery(metalinkEntries, uri, op.get(),
-                          op->get(PREF_METALINK_BASE_URI));
-  std::vector<SharedHandle<FileEntry> > fileEntries;
-  MetalinkEntry::toFileEntry(fileEntries, metalinkEntries);
-  util::toStream(fileEntries.begin(), fileEntries.end(), *global::cout());
-  global::cout()->write("\n");
-  global::cout()->flush();
-}
-} // namespace
-#endif // ENABLE_METALINK
-
-#if defined ENABLE_BITTORRENT || defined ENABLE_METALINK
-namespace {
-void showFiles
-(const std::vector<std::string>& uris, const SharedHandle<Option>& op)
-{
-  ProtocolDetector dt;
-  for(std::vector<std::string>::const_iterator i = uris.begin(),
-        eoi = uris.end(); i != eoi; ++i) {
-    printf(">>> ");
-    printf(MSG_SHOW_FILES, (*i).c_str());
-    printf("\n");
-    try {
-#ifdef ENABLE_BITTORRENT
-      if(dt.guessTorrentFile(*i)) {
-        showTorrentFile(*i);
-      } else
-#endif // ENABLE_BITTORRENT
-#ifdef ENABLE_METALINK
-        if(dt.guessMetalinkFile(*i)) {
-          showMetalinkFile(*i, op);
-        } else
-#endif // ENABLE_METALINK
-          {
-            printf(MSG_NOT_TORRENT_METALINK);
-            printf("\n\n");
-          }
-    } catch(RecoverableException& e) {
-      global::cout()->printf("%s\n", e.stackTrace().c_str());
-    }
-  }
-}
-} // namespace
-#endif // ENABLE_BITTORRENT || ENABLE_METALINK
-
-extern void option_processing(Option& option, std::vector<std::string>& uris,
-                              int argc, char* argv[]);
-
-error_code::Value main(int argc, char* argv[])
-{
-  std::vector<std::string> args;
-  SharedHandle<Option> op(new Option());
-  option_processing(*op.get(), args, argc, argv);
-
-  SimpleRandomizer::init();
-#ifdef ENABLE_BITTORRENT
-  bittorrent::generateStaticPeerId(op->get(PREF_PEER_ID_PREFIX));
-#endif // ENABLE_BITTORRENT
-  LogFactory::setLogFile(op->get(PREF_LOG));
-  LogFactory::setLogLevel(op->get(PREF_LOG_LEVEL));
-  LogFactory::setConsoleLogLevel(op->get(PREF_CONSOLE_LOG_LEVEL));
-  if(op->getAsBool(PREF_QUIET)) {
-    LogFactory::setConsoleOutput(false);
-  }
-  LogFactory::reconfigure();
+  Context context(argc, argv);
   error_code::Value exitStatus = error_code::FINISHED;
-  A2_LOG_INFO("<<--- --- --- ---");
-  A2_LOG_INFO("  --- --- --- ---");
-  A2_LOG_INFO("  --- --- --- --->>");
-  A2_LOG_INFO(fmt("%s %s %s", PACKAGE, PACKAGE_VERSION, TARGET));
-  A2_LOG_INFO(MSG_LOGGING_STARTED);
-
-  if(op->getAsBool(PREF_DISABLE_IPV6)) {
-    SocketCore::setProtocolFamily(AF_INET);
-    // Get rid of AI_ADDRCONFIG. It causes name resolution error
-    // when none of network interface has IPv4 address.
-    setDefaultAIFlags(0);
-  }
-  net::checkAddrconfig();
-  // Bind interface
-  if(!op->get(PREF_INTERFACE).empty()) {
-    std::string iface = op->get(PREF_INTERFACE);
-    SocketCore::bindAddress(iface);
-  }
-  sigset_t mask;
-#ifdef HAVE_SIGACTION
-  sigemptyset(&mask);
-#else // !HAVE_SIGACTION
-  mask = 0;
-#endif // !HAVE_SIGACTION
-#ifdef SIGPIPE
-  util::setGlobalSignalHandler(SIGPIPE, &mask, SIG_IGN, 0);
-#endif
-#ifdef SIGCHLD
-  // Avoid to create zombie process when forked child processes are
-  // died.
-  util::setGlobalSignalHandler(SIGCHLD, &mask, SIG_IGN, 0);
-#endif // SIGCHILD
-  std::vector<SharedHandle<RequestGroup> > requestGroups;
-  SharedHandle<UriListParser> uriListParser;
-#ifdef ENABLE_BITTORRENT
-  if(!op->blank(PREF_TORRENT_FILE)) {
-    if(op->get(PREF_SHOW_FILES) == A2_V_TRUE) {
-      showTorrentFile(op->get(PREF_TORRENT_FILE));
-      return exitStatus;
-    } else {
-      createRequestGroupForBitTorrent(requestGroups, op, args,
-                                      op->get(PREF_TORRENT_FILE));
-    }
-  }
-  else
-#endif // ENABLE_BITTORRENT
-#ifdef ENABLE_METALINK
-    if(!op->blank(PREF_METALINK_FILE)) {
-      if(op->get(PREF_SHOW_FILES) == A2_V_TRUE) {
-        showMetalinkFile(op->get(PREF_METALINK_FILE), op);
-        return exitStatus;
-      } else {
-        createRequestGroupForMetalink(requestGroups, op);
-      }
-    }
-    else
-#endif // ENABLE_METALINK
-      if(!op->blank(PREF_INPUT_FILE)) {
-        if(op->getAsBool(PREF_DEFERRED_INPUT)) {
-          uriListParser = openUriListParser(op->get(PREF_INPUT_FILE));
-        } else {
-          createRequestGroupForUriList(requestGroups, op);
-        }
-#if defined ENABLE_BITTORRENT || defined ENABLE_METALINK
-      } else if(op->get(PREF_SHOW_FILES) == A2_V_TRUE) {
-        showFiles(args, op);
-        return exitStatus;
-#endif // ENABLE_METALINK || ENABLE_METALINK
-      } else {
-        createRequestGroupForUri(requestGroups, op, args, false, false, true);
-      }
-
-  // Remove option values which is only valid for URIs specified in
-  // command-line. If they are left, because op is used as a template
-  // for new RequestGroup(such as created in RPC command), they causes
-  // unintentional effect.
-  for(SharedHandle<Option> i = op; i; i = i->getParent()) {
-    i->remove(PREF_OUT);
-    i->remove(PREF_FORCE_SEQUENTIAL);
-    i->remove(PREF_INPUT_FILE);
-    i->remove(PREF_INDEX_OUT);
-    i->remove(PREF_SELECT_FILE);
-    i->remove(PREF_PAUSE);
-    i->remove(PREF_CHECKSUM);
-    i->remove(PREF_GID);
-  }
-  if(!op->getAsBool(PREF_ENABLE_RPC) && requestGroups.empty() &&
-     !uriListParser) {
-    global::cout()->printf("%s\n", MSG_NO_FILES_TO_DOWNLOAD);
-  } else {
-    exitStatus = MultiUrlRequestInfo(requestGroups, op, getStatCalc(op),
-                                     getSummaryOut(op),
-                                     uriListParser).execute();
+  if(context.reqinfo) {
+    exitStatus = context.reqinfo->execute();
   }
   return exitStatus;
 }
 
 } // namespace aria2
 
-int main(int argc, char* argv[])
+int main(int argc, char** argv)
 {
   aria2::error_code::Value r;
   try {

+ 2 - 2
test/SingletonHolderTest.cc

@@ -40,8 +40,8 @@ public:
 
 void SingletonHolderTest::testInstance()
 {
-  M m("Hello world.");
-  SingletonHolder<M>::instance(&m);
+  SharedHandle<M> m(new M("Hello world."));
+  SingletonHolder<M>::instance(m);
   CPPUNIT_ASSERT_EQUAL(std::string("Hello world."),
                        SingletonHolder<M>::instance()->greeting());