diff --git a/src/DownloadEngineFactory.cc b/src/DownloadEngineFactory.cc index 5bd8d53d..9fd1db42 100644 --- a/src/DownloadEngineFactory.cc +++ b/src/DownloadEngineFactory.cc @@ -52,6 +52,7 @@ #include "AutoSaveCommand.h" #include "HaveEraseCommand.h" #include "TimedHaltCommand.h" +#include "WatchProcessCommand.h" #include "DownloadResult.h" #include "ServerStatMan.h" #include "a2io.h" @@ -163,6 +164,10 @@ DownloadEngineFactory::newDownloadEngine stopSec)); } } + if(op->defined(PREF_STOP_WITH_PROCESS)) { + unsigned int pid = op->getAsInt(PREF_STOP_WITH_PROCESS); + e->addRoutineCommand(new WatchProcessCommand(e->newCUID(), e.get(), pid)); + } if(op->getAsBool(PREF_ENABLE_RPC)) { bool ok = false; static int families[] = { AF_INET, AF_INET6 }; diff --git a/src/Makefile.am b/src/Makefile.am index 5d87e1ca..9a0e1493 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -223,7 +223,8 @@ SRCS = Socket.h\ NullHandle.h\ a2iterator.h\ paramed_string.cc paramed_string.h\ - rpc_helper.cc rpc_helper.h + rpc_helper.cc rpc_helper.h\ + WatchProcessCommand.cc WatchProcessCommand.h if MINGW_BUILD SRCS += WinConsoleFile.cc WinConsoleFile.h diff --git a/src/OptionHandlerFactory.cc b/src/OptionHandlerFactory.cc index 65277520..c784d58d 100644 --- a/src/OptionHandlerFactory.cc +++ b/src/OptionHandlerFactory.cc @@ -673,6 +673,15 @@ OptionHandlerFactory::createOptionHandlers() op->addTag(TAG_ADVANCED); handlers.push_back(op); } + { + SharedHandle op(new NumberOptionHandler + (PREF_STOP_WITH_PROCESS, + TEXT_STOP_WITH_PROCESS, + NO_DEFAULT_VALUE, + 0)); + op->addTag(TAG_ADVANCED); + handlers.push_back(op); + } { SharedHandle op(new NumberOptionHandler (PREF_SUMMARY_INTERVAL, diff --git a/src/WatchProcessCommand.cc b/src/WatchProcessCommand.cc new file mode 100644 index 00000000..b68c5ef7 --- /dev/null +++ b/src/WatchProcessCommand.cc @@ -0,0 +1,132 @@ +/* */ +/* + * Copyright 2011 Emmanuel Engelhart + * + * 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 3 of the License, or + * 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. + */ +#include "WatchProcessCommand.h" +#include "DownloadEngine.h" +#include "RequestGroupMan.h" +#include "LogFactory.h" +#include "Logger.h" +#include "fmt.h" + +#ifdef __APPLE__ +#import +#import +#define MIBSIZE 4 +#endif + +namespace aria2 { + +WatchProcessCommand::WatchProcessCommand +(cuid_t cuid, + DownloadEngine* e, + unsigned int pid, + bool forceHalt) + : TimeBasedCommand(cuid, e, 1, true), + pid_(pid), + forceHalt_(forceHalt) +{} + + +void WatchProcessCommand::preProcess() +{ + if(getDownloadEngine()->getRequestGroupMan()->downloadFinished() || + getDownloadEngine()->isHaltRequested()) { + enableExit(); + } +} + +void WatchProcessCommand::process() +{ + // Check process pid_ is running. If it is not running, shutdown + // aria2. + A2_LOG_DEBUG(fmt("Checking proess %u", pid_)); + bool waiting = true; +#ifdef _WIN32 + HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, pid_); + DWORD ret = WaitForSingleObject(process, 0); + CloseHandle(process); + if (ret != WAIT_TIMEOUT) { + waiting = false; + } +#elif __APPLE__ + int mib[MIBSIZE]; + struct kinfo_proc kp; + size_t len = sizeof(kp); + + mib[0]=CTL_KERN; + mib[1]=KERN_PROC; + mib[2]=KERN_PROC_PID; + mib[3]=pid_; + + int ret = sysctl(mib, MIBSIZE, &kp, &len, NULL, 0); + if (ret == -1 || len <= 0) { + waiting = false; + } +#else + if (access(fmt("/proc/%u", pid_).c_str(), F_OK) == -1) { + waiting = false; + } +#endif + if(!waiting) { + A2_LOG_INFO + (fmt("CUID#%lld - Process %u is not running. Commencing shutdown.", + getCuid(), pid_)); + if(forceHalt_) { + getDownloadEngine()->requestForceHalt(); + } else { + getDownloadEngine()->requestHalt(); + } + enableExit(); + } +} + +} // namespace aria2 diff --git a/src/WatchProcessCommand.h b/src/WatchProcessCommand.h new file mode 100644 index 00000000..e03833c4 --- /dev/null +++ b/src/WatchProcessCommand.h @@ -0,0 +1,62 @@ +/* */ +#ifndef D_WATCH_PROCESS_COMMAND_H +#define D_WATCH_PROCESS_COMMAND_H + +#include "TimeBasedCommand.h" + +namespace aria2 { + +class DownloadEngine; + +class WatchProcessCommand:public TimeBasedCommand { +public: + WatchProcessCommand + (cuid_t cuid, + DownloadEngine* e, + unsigned int pid, + bool forceHalt = false); + + virtual void preProcess(); + + virtual void process(); +private: + unsigned int pid_; + bool forceHalt_; +}; + +} // namespace aria2 + +#endif // D_WATCH_PROCESS_COMMAND_H diff --git a/src/prefs.cc b/src/prefs.cc index a8a95cc2..d2c2ad0b 100644 --- a/src/prefs.cc +++ b/src/prefs.cc @@ -321,6 +321,8 @@ const Pref* PREF_DOWNLOAD_RESULT = makePref("download-result"); const Pref* PREF_HASH_CHECK_ONLY = makePref("hash-check-only"); // values: hashType=digest const Pref* PREF_CHECKSUM = makePref("checksum"); +// value: pid +const Pref* PREF_STOP_WITH_PROCESS = makePref("stop-with-process"); /** * FTP related preferences diff --git a/src/prefs.h b/src/prefs.h index 562d89f0..e6d24635 100644 --- a/src/prefs.h +++ b/src/prefs.h @@ -276,6 +276,8 @@ extern const Pref* PREF_FTP_PASV; extern const Pref* PREF_FTP_REUSE_CONNECTION; // values: hashType=digest extern const Pref* PREF_CHECKSUM; +// value: pid +extern const Pref* PREF_STOP_WITH_PROCESS; /** * HTTP related preferences diff --git a/src/usage_text.h b/src/usage_text.h index 5c17c2fa..28b144d3 100644 --- a/src/usage_text.h +++ b/src/usage_text.h @@ -852,3 +852,10 @@ " option will be ignored in BitTorrent downloads.\n" \ " It will be also ignored if Metalink file\n" \ " contains piece hashes.") +#define TEXT_STOP_WITH_PROCESS \ + _(" --stop-with-process=PID Stop application when process PID is not running.\n" \ + " This is useful if aria2 process is forked from a\n" \ + " parent process. The parent process can fork aria2\n" \ + " with its own pid and when parent process exits\n" \ + " for some reason, aria2 can detect it and shutdown\n" \ + " itself.")