Skip to content
This repository has been archived by the owner on Nov 18, 2022. It is now read-only.

Commit

Permalink
#304: graceful termination of scheduler scripts
Browse files Browse the repository at this point in the history
If a script is running when the program must shutdown, the script
receives signal SIGINT (CTRL+BREAK on Windows) and has 10 seconds to
gracefully terminate until it is killed.
  • Loading branch information
hugbug committed Nov 15, 2016
1 parent 31208a5 commit e612257
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 4 deletions.
1 change: 1 addition & 0 deletions daemon/main/nzbget.cpp
Expand Up @@ -151,6 +151,7 @@ int main(int argc, char *argv[], char *argp[])
{
if (!strcmp(argv[i], "-D"))
{
AllocConsole(); // needed for sending CTRL+BREAK signal to child processes
StartService(RunMain);
return 0;
}
Expand Down
43 changes: 39 additions & 4 deletions daemon/util/Script.cpp
Expand Up @@ -304,6 +304,7 @@ int ScriptController::Execute()
PrepareEnvOptions(nullptr);
PrepareArgs();

m_completed = false;
int exitCode = 0;

#ifdef CHILD_WATCHDOG
Expand All @@ -315,6 +316,7 @@ int ScriptController::Execute()
int pipein = StartProcess();
if (pipein == -1)
{
m_completed = true;
return -1;
}

Expand All @@ -324,6 +326,7 @@ int ScriptController::Execute()
{
PrintMessage(Message::mkError, "Could not open pipe to %s", m_infoName);
close(pipein);
m_completed = true;
return -1;
}

Expand Down Expand Up @@ -404,7 +407,7 @@ int ScriptController::Execute()
#endif

debug("Exit code %i", exitCode);

m_completed = true;
return exitCode;
}

Expand Down Expand Up @@ -474,7 +477,7 @@ int ScriptController::StartProcess()
std::unique_ptr<wchar_t[]> environmentStrings = m_environmentStrings.GetStrings();

BOOL ok = CreateProcessW(nullptr, WString(cmdLine), nullptr, nullptr, TRUE,
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
NORMAL_PRIORITY_CLASS | CREATE_NEW_PROCESS_GROUP | CREATE_UNICODE_ENVIRONMENT,
environmentStrings.get(), wideWorkingDir, &startupInfo, &processInfo);
if (!ok)
{
Expand Down Expand Up @@ -505,6 +508,7 @@ int ScriptController::StartProcess()
debug("Child Process-ID: %i", (int)processInfo.dwProcessId);

m_processId = processInfo.hProcess;
m_dwProcessId = processInfo.dwProcessId;

// close unused "write" end
CloseHandle(writePipe);
Expand Down Expand Up @@ -636,7 +640,7 @@ void ScriptController::Terminate()
m_terminated = true;

#ifdef WIN32
BOOL ok = TerminateProcess(m_processId, -1);
BOOL ok = TerminateProcess(m_processId, -1) || m_completed;
if (ok)
{
// wait 60 seconds for process to terminate
Expand All @@ -655,7 +659,7 @@ void ScriptController::Terminate()
// if the child process has its own group (setsid() was successful), kill the whole group
killId = -killId;
}
bool ok = killId && kill(killId, SIGKILL) == 0;
bool ok = (killId && kill(killId, SIGKILL) == 0) || m_completed;
#endif

if (ok)
Expand All @@ -677,11 +681,42 @@ void ScriptController::TerminateAll()
{
if (script->m_processId && !script->m_detached)
{
// send break signal and wait up to 5 seconds for graceful termination
if (script->Break())
{
time_t curtime = Util::CurrentTime();
while (!script->m_completed && std::abs(curtime - Util::CurrentTime()) <= 10)
{
usleep(100 * 1000);
}
}
script->Terminate();
}
}
}

bool ScriptController::Break()
{
debug("Sending break signal to %s", m_infoName);

#ifdef WIN32
BOOL ok = GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, m_dwProcessId);
#else
bool ok = kill(m_processId, SIGINT) == 0;
#endif

if (ok)
{
debug("Sent break signal to %s", m_infoName);
}
else
{
warn("Could not send break signal to %s", m_infoName);
}

return ok;
}

void ScriptController::Detach()
{
debug("Detaching %s", m_infoName);
Expand Down
3 changes: 3 additions & 0 deletions daemon/util/Script.h
Expand Up @@ -54,6 +54,7 @@ class ScriptController
virtual ~ScriptController();
int Execute();
void Terminate();
bool Break();
void Resume();
void Detach();
static void TerminateAll();
Expand Down Expand Up @@ -91,10 +92,12 @@ class ScriptController
const char* m_logPrefix = nullptr;
EnvironmentStrings m_environmentStrings;
bool m_terminated = false;
bool m_completed = false;
bool m_detached = false;
FILE* m_readpipe;
#ifdef WIN32
HANDLE m_processId = 0;
DWORD m_dwProcessId = 0;
char m_cmdLine[2048];
#else
pid_t m_processId = 0;
Expand Down

0 comments on commit e612257

Please sign in to comment.