ProcessClass.php 7.55 KB
<?php

/**
 * @class ProcessClass
 * @brief Definition of a process
 * @details
 */
class ProcessClass
{
	private $command;
	private $postProcessCmd;
	private $getErrorMsgCmd;

	private $outputFile;
	private $exitCodeFile;
	private $processFile;
	private $execTimeFile;
	private $errorMsgFile;

	private $pID;
	private $runningPath;
	private $runningStart;
	private $fromWS;
	private $user;

	/*
	 * @brief Constructor
	*/
	function __construct($command, $postProcessCmd = "", $getErrorMsgCmd = "", $user = "", $fromWS = FALSE)
	{
		$this->command        = $command;
		$this->postProcessCmd = $postProcessCmd;
		$this->getErrorMsgCmd = $getErrorMsgCmd;
		$this->pID            = 0;
		$this->fromWS         = $fromWS;
		$this->user           = $user;
	}

	/*
	 * @brief Init an existing process
	*/
	public function init($outputFile, $exitCodeFile, $processFile, $execTimeFile, $errorMsgFile, $pid, $runningPath, $runningStart)
	{
		$this->outputFile   = $outputFile;
		$this->exitCodeFile = $exitCodeFile;
		$this->processFile  = $processFile;
		$this->execTimeFile = $execTimeFile;
		$this->errorMsgFile = $errorMsgFile;
		$this->pID          = $pid;
		$this->runningPath  = $runningPath;
		$this->runningStart = $runningStart;
	}

	/*
	 * @brief Get the command of the process
	*/
	public function getCommand()
	{
		return $this->command;
	}

	/*
	 * @brief Get the output file of the process
	*/
	public function getOutputFile()
	{
		return $this->outputFile;
	}

	/*
	 * @brief Get the exit code file of a process
	*/
	public function getExitCodeFile()
	{
		return $this->exitCodeFile;
	}

	/*
	 * @brief Get the process file
	*/
	public function getProcessFile()
	{
		return $this->processFile;
	}

	/*
	 * @brief Get execution time of a process
	 */
	public function getExecTimeFile()
	{
		return $this->execTimeFile;
	}

	/*
	 * @brief Get the error msg file
	 */
	public function getErrorMsgFile()
	{
		return $this->errorMsgFile;
	}

	/*
	 * @brief Get the running path of the request
	*/
	public function getRunningPath()
	{
		return $this->runningPath;
	}

	/*
	 * @brief Get the start date of a process
	*/
	public function getRunningStart()
	{
		return $this->runningStart;
	}

	/*
	 * @brief Get the exit code of the process
	*/
	public function getExitCode()
	{
		if ($this->pID == 0)
			return -1000;
		if ($this->isRunning())
			return -1001;
		if (!chdir($this->runningPath))
			return -1002;
		if (!file_exists($this->exitCodeFile))
			return -1003;
		$result = file_get_contents($this->exitCodeFile);

		if ($result === false)
			return -1004;

		return intval($result);
	}

	public function getErrorMsg()
	{
		switch ($this->getExitCode()) {
			case 0:
				return ""; //No error
			case -1000:
				return "No process Id";
			case -1001:
				return "Request is running.";
			case -1002:
				return "Cannot access to the request directory";
			case -1003:
				return "Cannot retrieve exit code file";
			case -1004:
				return "Cannot read exit code file";
		}
		if (!file_exists($this->errorMsgFile)) {
			return "Cannot retrieve error message file";
		}
		$result = file_get_contents($this->errorMsgFile);

		if ($result === false)
			return "Cannot read error message file";

		$result = trim($result);
		if (empty($result))
			return "Unknown error";

		return $result;
	}

	/*
	 * @brief Get execution time of the process
	 */
	public function getExecTime()
	{
		if ($this->getExitCode() != 0)
			return 0;
		$result = file_get_contents($this->execTimeFile);

		if ($result === false)
			return 0;

		return intval($result);
	}

	/*
	 * @brief Get the PID of the process
	*/
	public function getPID()
	{
		return $this->pID;
	}

	/*
	 * @brief To know if a process is running from WebService
	 */
	public function getFromWS()
	{
		return $this->fromWS;
	}

	/*
	 * @brief Get user that run the request. Empty if not known.
	 */
	public function getUser()
	{
		return $this->user;
	}

	/*
	 * @brief Run the process
	*/
	public function run($runningPath, $envArray)
	{
		if ($this->isRunning())
			return false;

		if (!is_dir($runningPath))
			return false;

		$this->runningPath = $runningPath;

		if (!$this->initRun($envArray))
			return false;

		$cmd = 'nohup ./' . $this->processFile . ' > /dev/null 2>&1 & echo $!';

		exec($cmd, $op);
		$this->pID = (int)$op[0];
		$this->runningStart = time();

		return ($this->pID > 0);
	}

	/*
	 * @brief Wait end of the process execution
	*/
	public function waitEndOfProcess($callback, $batchEnabled)
	{
		while ($this->isRunning()) {
			if (!call_user_func($callback, $this, $batchEnabled))
				return false;
			usleep(100000);
		}
		return true;
	}

	/*
	 * @brief Test if the process is running
	*/
	public function isRunning()
	{
		if ($this->pID == 0)
			return false;
		$cmd = 'ps -p ' . $this->pID;
		exec($cmd, $op);
		return isset($op[1]);
	}

	/*
	 * @brief Stop the process execution
	*/
	public function stop()
	{
		//Kill process and all child processes
		$cmd = "pkill -TERM -P $this->pID";
		//$cmd = 'kill '.$this->pID;
		exec($cmd);
		return TRUE;
	}

	/*
	 * @brief Delete the process
	*/
	public function delete($keep_log)
	{
		if (!$this->stop())
			return false;

		if (!chdir($this->runningPath))
			return false;

		if (!$keep_log && file_exists($this->outputFile))
			unlink($this->outputFile);

		if (file_exists($this->exitCodeFile))
			unlink($this->exitCodeFile);

		if (file_exists($this->processFile))
			unlink($this->processFile);

		if (file_exists($this->execTimeFile))
			unlink($this->execTimeFile);

		if (file_exists($this->errorMsgFile))
			unlink($this->errorMsgFile);

		return true;
	}

	/*
	 * @brief Init the execution of a process
	*/
	private function initRun($envArray)
	{
		if ($this->isRunning())
			return false;

		if (!chdir($this->runningPath))
			return false;

		$this->outputFile   = "cmd_output";
		$this->exitCodeFile = "cmd_exitcode";
		$this->processFile  = "cmd_process";
		$this->execTimeFile = "cmd_exec_time";
		$this->errorMsgFile = "cmd_errormsg";

		if (file_exists($this->outputFile))
			unlink($this->outputFile);

		if (file_exists($this->exitCodeFile))
			unlink($this->exitCodeFile);

		if (file_exists($this->processFile))
			unlink($this->processFile);

		if (file_exists($this->execTimeFile))
			unlink($this->execTimeFile);

		if (file_exists($this->errorMsgFile))
			unlink($this->errorMsgFile);

		file_put_contents($this->processFile, "#!/bin/bash" . PHP_EOL);
		foreach ($envArray as $key => $value)
			file_put_contents($this->processFile, "export " . $key . "=" . $value . PHP_EOL, FILE_APPEND);
		file_put_contents($this->processFile, "CMD_START=`echo $(($(date +%s%N)/1000000))`" . PHP_EOL, FILE_APPEND);
		file_put_contents($this->processFile, $this->command . " > " . $this->outputFile . " 2>&1" . PHP_EOL, FILE_APPEND);
		file_put_contents($this->processFile, "ERROR_CODE=$?" . PHP_EOL, FILE_APPEND);
		file_put_contents($this->processFile, "echo \$ERROR_CODE > " . $this->exitCodeFile . PHP_EOL, FILE_APPEND);
		file_put_contents($this->processFile, "if [ \$ERROR_CODE -ne 0 ] && [ \"" . $this->getErrorMsgCmd . "\" != \"\" ]; then" . PHP_EOL, FILE_APPEND);
		file_put_contents($this->processFile, "    " . $this->getErrorMsgCmd . " > " . $this->errorMsgFile . PHP_EOL, FILE_APPEND);
		file_put_contents($this->processFile, "fi;" . PHP_EOL, FILE_APPEND);
		file_put_contents($this->processFile, "CMD_STOP=`echo $(($(date +%s%N)/1000000))`" . PHP_EOL, FILE_APPEND);
		file_put_contents($this->processFile, "echo $((\$CMD_STOP-\$CMD_START)) > " . $this->execTimeFile . PHP_EOL, FILE_APPEND);
		if ($this->postProcessCmd != "")
			file_put_contents($this->processFile, $this->postProcessCmd . PHP_EOL, FILE_APPEND);

		chmod($this->processFile, 0755);

		return true;
	}
}