From b89bc8a6cb3b93997647ceefc5b17bf05f20a0d0 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Thu, 1 Dec 2011 22:53:30 +0900 Subject: [PATCH] Added --stop-with-process=PID option. This feature stops application when process PID is not running. This is useful if aria2 process is forked from a parent process. The parent process can fork aria2 with its own pid and when parent process exits for some reason, aria2 can detect it and shutdown itself. The code which detects whether or not given process PID is running is contributed by Emmanuel Engelhart. --- src/DownloadEngineFactory.cc | 5 ++ src/Makefile.am | 3 +- src/OptionHandlerFactory.cc | 9 +++ src/WatchProcessCommand.cc | 132 +++++++++++++++++++++++++++++++++++ src/WatchProcessCommand.h | 62 ++++++++++++++++ src/prefs.cc | 2 + src/prefs.h | 2 + src/usage_text.h | 7 ++ 8 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 src/WatchProcessCommand.cc create mode 100644 src/WatchProcessCommand.h 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.")