void NZBScriptController::PrepareEnvScript(NZBParameterList* pParameters, const char* szScriptName) { if (pParameters) { PrepareEnvParameters(pParameters, NULL); } char szParamPrefix[1024]; snprintf(szParamPrefix, 1024, "%s:", szScriptName); szParamPrefix[1024-1] = '\0'; if (pParameters) { PrepareEnvParameters(pParameters, szParamPrefix); } PrepareEnvOptions(szParamPrefix); }
int ScriptController::Execute() { PrepareEnvOptions(nullptr); PrepareArgs(); m_completed = false; int exitCode = 0; #ifdef CHILD_WATCHDOG bool childConfirmed = false; while (!childConfirmed && !m_terminated) { #endif int pipein = -1, pipeout = -1; StartProcess(&pipein, &pipeout); if (pipein == -1) { m_completed = true; return -1; } // open the read end m_readpipe = fdopen(pipein, "r"); if (!m_readpipe) { PrintMessage(Message::mkError, "Could not open read pipe to %s", *m_infoName); close(pipein); close(pipeout); m_completed = true; return -1; } m_writepipe = 0; if (m_needWrite) { // open the write end m_writepipe = fdopen(pipeout, "w"); if (!m_writepipe) { PrintMessage(Message::mkError, "Could not open write pipe to %s", *m_infoName); close(pipein); close(pipeout); m_completed = true; return -1; } } #ifdef CHILD_WATCHDOG debug("Creating child watchdog"); ChildWatchDog watchDog; watchDog.SetAutoDestroy(false); watchDog.SetProcessId(m_processId); watchDog.SetInfoName(m_infoName); watchDog.Start(); #endif CharBuffer buf(1024 * 10); debug("Entering pipe-loop"); bool firstLine = true; bool startError = false; while (!m_terminated && !m_detached && !feof(m_readpipe)) { if (ReadLine(buf, buf.Size(), m_readpipe) && m_readpipe) { #ifdef CHILD_WATCHDOG if (!childConfirmed) { childConfirmed = true; watchDog.Stop(); debug("Child confirmed"); continue; } #endif if (firstLine && !strncmp(buf, "[ERROR] Could not start ", 24)) { startError = true; } ProcessOutput(buf); firstLine = false; } } debug("Exited pipe-loop"); #ifdef CHILD_WATCHDOG debug("Destroying WatchDog"); if (!childConfirmed) { watchDog.Stop(); } while (watchDog.IsRunning()) { Util::Sleep(5); } #endif if (m_readpipe) { fclose(m_readpipe); } if (m_writepipe) { fclose(m_writepipe); } if (m_terminated && m_infoName) { warn("Interrupted %s", *m_infoName); } exitCode = 0; if (!m_detached) { exitCode = WaitProcess(); #ifndef WIN32 if (exitCode == FORK_ERROR_EXIT_CODE && startError) { exitCode = -1; } #endif } #ifdef CHILD_WATCHDOG } // while (!bChildConfirmed && !m_bTerminated) #endif debug("Exit code %i", exitCode); m_completed = true; return exitCode; }
int ScriptController::Execute() { PrepareEnvOptions(NULL); PrepareArgs(); int iExitCode = 0; int pipein; #ifdef CHILD_WATCHDOG bool bChildConfirmed = false; while (!bChildConfirmed && !m_bTerminated) { #endif #ifdef WIN32 // build command line char* szCmdLine = NULL; if (m_szArgs) { char szCmdLineBuf[2048]; int iUsedLen = 0; for (const char** szArgPtr = m_szArgs; *szArgPtr; szArgPtr++) { snprintf(szCmdLineBuf + iUsedLen, 2048 - iUsedLen, "\"%s\" ", *szArgPtr); iUsedLen += strlen(*szArgPtr) + 3; } szCmdLineBuf[iUsedLen < 2048 ? iUsedLen - 1 : 2048 - 1] = '\0'; szCmdLine = szCmdLineBuf; } else { szCmdLine = m_szCmdLine; } // create pipes to write and read data HANDLE hReadPipe, hWritePipe; SECURITY_ATTRIBUTES SecurityAttributes; memset(&SecurityAttributes, 0, sizeof(SecurityAttributes)); SecurityAttributes.nLength = sizeof(SecurityAttributes); SecurityAttributes.bInheritHandle = TRUE; CreatePipe(&hReadPipe, &hWritePipe, &SecurityAttributes, 0); SetHandleInformation(hReadPipe, HANDLE_FLAG_INHERIT, 0); STARTUPINFO StartupInfo; memset(&StartupInfo, 0, sizeof(StartupInfo)); StartupInfo.cb = sizeof(StartupInfo); StartupInfo.dwFlags = STARTF_USESTDHANDLES; StartupInfo.hStdInput = 0; StartupInfo.hStdOutput = hWritePipe; StartupInfo.hStdError = hWritePipe; PROCESS_INFORMATION ProcessInfo; memset(&ProcessInfo, 0, sizeof(ProcessInfo)); char* szEnvironmentStrings = m_environmentStrings.GetStrings(); BOOL bOK = CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, szEnvironmentStrings, m_szWorkingDir, &StartupInfo, &ProcessInfo); if (!bOK) { DWORD dwErrCode = GetLastError(); char szErrMsg[255]; szErrMsg[255-1] = '\0'; if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErrCode, 0, szErrMsg, 255, NULL)) { PrintMessage(Message::mkError, "Could not start %s: %s", m_szInfoName, szErrMsg); } else { PrintMessage(Message::mkError, "Could not start %s: error %i", m_szInfoName, dwErrCode); } if (!Util::FileExists(m_szScript)) { PrintMessage(Message::mkError, "Could not find file %s", m_szScript); } free(szEnvironmentStrings); return -1; } free(szEnvironmentStrings); debug("Child Process-ID: %i", (int)ProcessInfo.dwProcessId); m_hProcess = ProcessInfo.hProcess; // close unused "write" end CloseHandle(hWritePipe); pipein = _open_osfhandle((intptr_t)hReadPipe, _O_RDONLY); #else int p[2]; int pipeout; // create the pipe if (pipe(p)) { PrintMessage(Message::mkError, "Could not open pipe: errno %i", errno); return -1; } char** pEnvironmentStrings = m_environmentStrings.GetStrings(); pipein = p[0]; pipeout = p[1]; debug("forking"); pid_t pid = fork(); if (pid == -1) { PrintMessage(Message::mkError, "Could not start %s: errno %i", m_szInfoName, errno); free(pEnvironmentStrings); return -1; } else if (pid == 0) { // here goes the second instance // create new process group (see Terminate() where it is used) setsid(); // close up the "read" end close(pipein); // make the pipeout to be the same as stdout and stderr dup2(pipeout, 1); dup2(pipeout, 2); close(pipeout); #ifdef CHILD_WATCHDOG fwrite("\n", 1, 1, stdout); fflush(stdout); #endif chdir(m_szWorkingDir); environ = pEnvironmentStrings; execvp(m_szScript, (char* const*)m_szArgs); if (errno == EACCES) { fprintf(stdout, "[WARNING] Fixing permissions for %s\n", m_szScript); fflush(stdout); Util::FixExecPermission(m_szScript); execvp(m_szScript, (char* const*)m_szArgs); } // NOTE: the text "[ERROR] Could not start " is checked later, // by changing adjust the dependent code below. fprintf(stdout, "[ERROR] Could not start %s: %s", m_szScript, strerror(errno)); fflush(stdout); _exit(254); } // continue the first instance debug("forked"); debug("Child Process-ID: %i", (int)pid); free(pEnvironmentStrings); m_hProcess = pid; // close unused "write" end close(pipeout); #endif // open the read end m_pReadpipe = fdopen(pipein, "r"); if (!m_pReadpipe) { PrintMessage(Message::mkError, "Could not open pipe to %s", m_szInfoName); return -1; } #ifdef CHILD_WATCHDOG debug("Creating child watchdog"); ChildWatchDog* pWatchDog = new ChildWatchDog(); pWatchDog->SetAutoDestroy(false); pWatchDog->SetProcessID(pid); pWatchDog->Start(); #endif char* buf = (char*)malloc(10240); debug("Entering pipe-loop"); bool bFirstLine = true; bool bStartError = false; while (!m_bTerminated && !m_bDetached && !feof(m_pReadpipe)) { if (ReadLine(buf, 10240, m_pReadpipe) && m_pReadpipe) { #ifdef CHILD_WATCHDOG if (!bChildConfirmed) { bChildConfirmed = true; pWatchDog->Stop(); debug("Child confirmed"); continue; } #endif if (bFirstLine && !strncmp(buf, "[ERROR] Could not start ", 24)) { bStartError = true; } ProcessOutput(buf); bFirstLine = false; } } debug("Exited pipe-loop"); #ifdef CHILD_WATCHDOG debug("Destroying WatchDog"); if (!bChildConfirmed) { pWatchDog->Stop(); } while (pWatchDog->IsRunning()) { usleep(5 * 1000); } delete pWatchDog; #endif free(buf); if (m_pReadpipe) { fclose(m_pReadpipe); } if (m_bTerminated) { warn("Interrupted %s", m_szInfoName); } iExitCode = 0; if (!m_bTerminated && !m_bDetached) { #ifdef WIN32 WaitForSingleObject(m_hProcess, INFINITE); DWORD dExitCode = 0; GetExitCodeProcess(m_hProcess, &dExitCode); iExitCode = dExitCode; #else int iStatus = 0; waitpid(m_hProcess, &iStatus, 0); if (WIFEXITED(iStatus)) { iExitCode = WEXITSTATUS(iStatus); if (iExitCode == 254 && bStartError) { iExitCode = -1; } } #endif } #ifdef CHILD_WATCHDOG } // while (!bChildConfirmed && !m_bTerminated) #endif debug("Exit code %i", iExitCode); return iExitCode; }