PageRenderTime 53ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/plugins/_task/task.php

https://gitlab.com/billyprice1/QuickBox
PHP | 295 lines | 288 code | 7 blank | 0 comment | 13 complexity | 740d75dcb85a3d1d69fee99bfa0b372f MD5 | raw file
  1. <?php
  2. require_once( dirname(__FILE__)."/../../php/xmlrpc.php" );
  3. class rTask
  4. {
  5. const MAX_CONSOLE_SIZE = 80;
  6. const MAX_ARG_LENGTH = 2048;
  7. const FLG_WAIT = 0x0001;
  8. const FLG_STRIP_LOGS = 0x0002;
  9. const FLG_ONE_LOG = 0x0004;
  10. const FLG_ECHO_CMD = 0x0008;
  11. const FLG_DEFAULT = 0x000A;
  12. const FLG_NO_ERR = 0x0010;
  13. const FLG_RUN_AS_WEB = 0x0020;
  14. const FLG_RUN_AS_CMD = 0x0040;
  15. const FLG_STRIP_ERRS = 0x0080;
  16. const FLG_NO_LOG = 0x0100;
  17. const FLG_REMOVE_ASCII = 0x0200;
  18. public $params = array();
  19. public $id = 0;
  20. public function __construct( $params, $taskNo = null )
  21. {
  22. $this->params = $params;
  23. $this->id = $taskNo;
  24. if(empty($this->id))
  25. $this->id = uniqid( time(), true );
  26. }
  27. static public function formatPath( $taskNo )
  28. {
  29. return( getSettingsPath().'/tasks/'.$taskNo );
  30. }
  31. public function makeDirectory()
  32. {
  33. $dir = self::formatPath($this->id);
  34. makeDirectory($dir);
  35. return($dir);
  36. }
  37. public function start( $commands, $flags = self::FLG_DEFAULT )
  38. {
  39. if(!rTorrentSettings::get()->linkExist)
  40. $flags|=self::FLG_RUN_AS_WEB;
  41. if(count($commands))
  42. {
  43. $dir = $this->makeDirectory();
  44. if(($sh = fopen($dir."/start.sh","w"))!==false)
  45. {
  46. fputs($sh,'#!/bin/sh'."\n");
  47. fputs($sh,'dir="$(dirname $0)"'."\n");
  48. fputs($sh,'echo $$ > "${dir}"/pid'."\n");
  49. fputs($sh,'chmod a+rw "${dir}"/pid'."\n");
  50. file_put_contents($dir."/flags",$flags);
  51. @chmod($dir."/flags",0666);
  52. fputs($sh,'touch "${dir}"/status'."\n");
  53. fputs($sh,'chmod a+rw "${dir}"/status'."\n");
  54. fputs($sh,'touch "${dir}"/errors'."\n");
  55. fputs($sh,'chmod a+rw "${dir}"/errors'."\n");
  56. fputs($sh,'touch "${dir}"/log'."\n");
  57. fputs($sh,'chmod a+rw "${dir}"/log'."\n");
  58. fputs($sh,'last=0'."\n");
  59. $err = ($flags & self::FLG_ONE_LOG) ? "log" : "errors";
  60. foreach( $commands as $ndx=>$cmd )
  61. {
  62. if($cmd=='{')
  63. fputs($sh,'if [ $last -eq 0 ] ; then '."\n");
  64. else
  65. if($cmd=='}')
  66. fputs($sh,'fi'."\n");
  67. else
  68. if($cmd=='!{')
  69. fputs($sh,'if [ $last -ne 0 ] ; then '."\n");
  70. else
  71. if($cmd[0]=='>')
  72. fputs($sh,'echo '.escapeshellarg(substr($cmd,1)).' >> "${dir}"/log'."\n");
  73. else
  74. {
  75. if($flags & self::FLG_ECHO_CMD)
  76. fputs($sh,'echo '.escapeshellarg($cmd).' >> "${dir}"/log'."\n");
  77. if($flags & self::FLG_NO_ERR)
  78. fputs($sh,$cmd.' >> "${dir}"/log'."\n");
  79. else
  80. if($flags & self::FLG_NO_LOG)
  81. fputs($sh,$cmd.' >> "${dir}"/errors 2>> "${dir}"/errors'."\n");
  82. else
  83. fputs($sh,$cmd.' 2>> "${dir}"/'.$err.' >> "${dir}"/log'."\n");
  84. fputs($sh,'if [ $? -ne 0 ] ; then '."\n\t".'last=1'."\n".'fi'."\n");
  85. }
  86. }
  87. fputs($sh,'echo $last > "${dir}"/status'."\n");
  88. fclose($sh);
  89. @chmod($dir."/start.sh",0755);
  90. if(!self::run($dir."/start.sh",$flags))
  91. {
  92. file_put_contents( $dir."/params", serialize($this->params) );
  93. if(!($flags & self::FLG_WAIT))
  94. sleep(1);
  95. return(self::check($this->id,$flags));
  96. }
  97. }
  98. self::clean($dir);
  99. }
  100. return(array( "no"=>$this->id, "pid"=>0, "status"=>255, "log"=>array(), "errors"=>array(count($commands) ? "Can't start operation" : "Incorrect target directory") ));
  101. }
  102. static public function clean( $dir )
  103. {
  104. @deleteDirectory( $dir );
  105. }
  106. static protected function removeASCII( $subject )
  107. {
  108. $subject = preg_replace('/\x1b(\[|\(|\))[;?0-9]*[0-9A-Za-z]/', "",$subject);
  109. $subject = preg_replace('/\x1b(\[|\(|\))[;?0-9]*[0-9A-Za-z]/', "",$subject);
  110. $subject = preg_replace('/[\x03|\x1a]/', "", $subject);
  111. return($subject);
  112. }
  113. static protected function processLog( $dir, $logName, &$ret, $stripConsole, $removeASCII )
  114. {
  115. if(is_file($dir.'/'.$logName) && is_readable($dir.'/'.$logName))
  116. {
  117. $lines = file($dir.'/'.$logName);
  118. foreach( $lines as $line )
  119. {
  120. // if($stripConsole)
  121. {
  122. $pos = strrpos($line,"\r");
  123. if($pos!==false)
  124. {
  125. $line = rtrim(substr($line,$pos+1));
  126. if(strlen($line)==0)
  127. continue;
  128. }
  129. if(strrpos($line,chr(8))!==false)
  130. {
  131. $len = strlen($line);
  132. $res = array();
  133. for($i=0; $i<$len; $i++)
  134. {
  135. if($line[$i]==chr(8))
  136. array_pop($res);
  137. else
  138. $res[] = $line[$i];
  139. }
  140. $line = implode('',$res);
  141. }
  142. }
  143. if($removeASCII)
  144. $line = self::removeASCII( $line );
  145. $ret[$logName][] = rtrim($line);
  146. }
  147. if($stripConsole && (count($ret[$logName])>self::MAX_CONSOLE_SIZE))
  148. array_splice($ret[$logName],0,count($ret[$logName])-self::MAX_CONSOLE_SIZE);
  149. }
  150. }
  151. static public function check( $taskNo, $flags = null )
  152. {
  153. $dir = self::formatPath($taskNo);
  154. $ret = array( "no"=>$taskNo, "pid"=>0, "status"=>-1, "log"=>array(), "errors"=>array(), "params"=>null, "start"=>@filemtime($dir.'/pid'), "finish"=>0 );
  155. if(is_file($dir.'/pid') && is_readable($dir.'/pid'))
  156. {
  157. if(is_null($flags))
  158. $flags = intval(file_get_contents($dir.'/flags'));
  159. $ret["pid"] = intval(trim(file_get_contents($dir.'/pid')));
  160. if(is_file($dir.'/status') && is_readable($dir.'/status'))
  161. {
  162. $status = trim(file_get_contents($dir.'/status'));
  163. if(strlen($status))
  164. {
  165. $ret["status"] = intval($status);
  166. $ret["finish"] = filemtime($dir.'/status');
  167. }
  168. }
  169. if(is_file($dir.'/params') && is_readable($dir.'/params'))
  170. $ret["params"] = unserialize(file_get_contents($dir.'/params'));
  171. self::processLog($dir, 'log', $ret, ($flags & self::FLG_STRIP_LOGS), ($flags & self::FLG_REMOVE_ASCII));
  172. self::processLog($dir, 'errors', $ret, ($flags & self::FLG_STRIP_ERRS), ($flags & self::FLG_REMOVE_ASCII));
  173. }
  174. return($ret);
  175. }
  176. static public function run( $cmd, $flags = 0 )
  177. {
  178. $ret = -1;
  179. $params = " >/dev/null 2>&1";
  180. if(!($flags & self::FLG_WAIT))
  181. $params.=" &";
  182. if($flags & self::FLG_RUN_AS_WEB)
  183. {
  184. if(self::FLG_RUN_AS_CMD)
  185. $cmd = '-c "'.$cmd.'"';
  186. exec('sh '.$cmd.$params, $output, $ret);
  187. }
  188. else
  189. {
  190. $req = new rXMLRPCRequest(
  191. ((rTorrentSettings::get()->iVersion>=0x900) && !($flags & self::FLG_WAIT)) ?
  192. new rXMLRPCCommand( "execute.nothrow.bg", array("","sh",$cmd) ) :
  193. new rXMLRPCCommand( "execute_nothrow", array("sh","-c",$cmd.$params) )
  194. );
  195. if($req->success() && count($req->val))
  196. $ret = intval($req->val[0]);
  197. }
  198. return($ret);
  199. }
  200. static public function kill( $taskNo, $flags = null )
  201. {
  202. $dir = self::formatPath($taskNo);
  203. $ret = array( "no"=>$taskNo, "pid"=>0, "status"=>-1, "log"=>array(), "errors"=>array() );
  204. if(is_file($dir.'/pid') && is_readable($dir.'/pid'))
  205. {
  206. if(is_file($dir.'/status') && is_readable($dir.'/status'))
  207. {
  208. $status = trim(file_get_contents($dir.'/status'));
  209. if(strlen($status))
  210. $ret["status"] = intval($status);
  211. }
  212. if($ret["status"]<0)
  213. {
  214. if(is_null($flags))
  215. $flags = intval(file_get_contents($dir.'/flags'));
  216. $pid = trim(file_get_contents($dir.'/pid'));
  217. self::run("kill -9 `".getExternal("pgrep")." -P ".$pid."` ; kill -9 ".$pid, ($flags & self::FLG_RUN_AS_WEB) | self::FLG_WAIT | self::FLG_RUN_AS_CMD );
  218. }
  219. self::clean($dir);
  220. }
  221. return(true);
  222. }
  223. }
  224. class rTaskManager
  225. {
  226. static public function obtain()
  227. {
  228. $tasks = array();
  229. $dir = getSettingsPath().'/tasks/';
  230. if( $handle = @opendir($dir) )
  231. {
  232. while(false !== ($file = readdir($handle)))
  233. {
  234. if($file != "." && $file != ".." && is_dir($dir.$file))
  235. {
  236. $tasks[$file] = rTask::check( $file );
  237. $tasks[$file]["name"] = $tasks[$file]["params"]["name"];
  238. $tasks[$file]["requester"] = $tasks[$file]["params"]["requester"];
  239. unset($tasks[$file]["params"]["name"]);
  240. unset($tasks[$file]["params"]["requester"]);
  241. }
  242. }
  243. closedir($handle);
  244. }
  245. return($tasks);
  246. }
  247. static public function isPIDExists( $pid )
  248. {
  249. return( function_exists( 'posix_getpgid' ) ? (posix_getpgid($pid)!==false) : file_exists( '/proc/'.$pid ) );
  250. }
  251. static public function cleanup()
  252. {
  253. $tasks = self::obtain();
  254. foreach( $tasks as $id=>$task )
  255. {
  256. if( ($task["status"]<0) && (!$task["pid"] || !self::isPIDExists($task["pid"])) )
  257. rTask::clean(rTask::formatPath($id));
  258. }
  259. }
  260. static public function remove( $list )
  261. {
  262. $tasks = array();
  263. $dir = getSettingsPath().'/tasks/';
  264. if( $handle = @opendir($dir) )
  265. {
  266. while(false !== ($file = readdir($handle)))
  267. {
  268. if($file != "." && $file != ".." && is_dir($dir.$file) && in_array($file,$list))
  269. $tasks[] = $file;
  270. }
  271. closedir($handle);
  272. foreach( $tasks as $id )
  273. rTask::kill( $id );
  274. $tasks = self::obtain();
  275. }
  276. return($tasks);
  277. }
  278. }