PageRenderTime 55ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/test/netbatch.php

https://github.com/blekkzor/pinetd2
PHP | 447 lines | 320 code | 85 blank | 42 comment | 56 complexity | 82c5c284900d57e1a7bb9140a7511f76 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. class NetBatch_Process {
  3. private $pid;
  4. private $parent;
  5. private $blocking;
  6. private $resume;
  7. public function __construct($parent, $pid, $resume, $persist) {
  8. $this->parent = $parent;
  9. $this->pid = $pid;
  10. $this->blocking = true;
  11. $this->resume = $resume;
  12. $this->persist = $persist;
  13. }
  14. public function __destruct() {
  15. if ($this->running())
  16. $this->kill(9); // SIGKILL
  17. $this->parent->freePid($this->pid);
  18. }
  19. public function setBlocking($blocking) {
  20. $this->blocking = (bool)$blocking;
  21. }
  22. public function dump() {
  23. return $this->parent->dump($this->pid);
  24. }
  25. public function eof($fd) {
  26. return $this->parent->eof($fd, $this->pid);
  27. }
  28. public function read($fd, $size) {
  29. return $this->parent->read($fd, $size, $this->pid, $this->blocking);
  30. }
  31. public function gets($fd, $size = NULL) {
  32. return $this->parent->gets($fd, $size, $this->pid, $this->blocking);
  33. }
  34. public function wait() {
  35. return $this->parent->wait($this->pid);
  36. }
  37. public function write($fd, $buf) {
  38. return $this->parent->write($fd, $buf, $this->pid);
  39. }
  40. public function kill($signal = 15) {
  41. return $this->parent->kill($signal, $this->pid);
  42. }
  43. public function close($fd) {
  44. return $this->parent->close($fd, $this->pid);
  45. }
  46. public function getPid() {
  47. return $this->pid;
  48. }
  49. public function poll() {
  50. return $this->parent->poll($this->pid);
  51. }
  52. public function isResumed() {
  53. return (bool)$this->resume;
  54. }
  55. public function running() {
  56. return $this->parent->isRunning($this->pid);
  57. }
  58. public function exitCode() {
  59. return $this->parent->getExitCode($this->pid);
  60. }
  61. }
  62. class NetBatch {
  63. private $fp;
  64. private $name;
  65. private $salt;
  66. private $type;
  67. private $pipes = array();
  68. private $pipes_buf = array();
  69. private $running = array();
  70. private $returnCode = array();
  71. private $persist = array();
  72. private $last_pid;
  73. const PKT_STANDARD = 0;
  74. const PKT_LOGIN = 1;
  75. const PKT_EXIT = 2;
  76. const PKT_RUN = 3;
  77. const PKT_RETURNCODE = 4;
  78. const PKT_EOF = 5;
  79. const PKT_CLOSE = 6;
  80. const PKT_DATA = 7;
  81. const PKT_NOPIPES = 8;
  82. const PKT_KILL = 9;
  83. const PKT_POLL = 10;
  84. public function __construct($host, $port = 65432) {
  85. $this->fp = fsockopen($host, $port);
  86. if (!$this->fp) throw new Exception('Could not connect');
  87. $this->name = $this->readPacket();
  88. $this->salt = $this->readPacket();
  89. }
  90. public function __destruct() {
  91. $this->sendPacket('', self::PKT_EXIT);
  92. fclose($this->fp);
  93. }
  94. public function freePid($pid) {
  95. unset($this->pipes[$pid]);
  96. unset($this->pipes_buf[$pid]);
  97. unset($this->returnCode[$pid]);
  98. }
  99. public function ident($login, $key) {
  100. $this->sendPacket(sha1($key.$this->salt, true).$login, self::PKT_LOGIN);
  101. $result = $this->readPacket();
  102. return (bool)$result;
  103. }
  104. public function run($cmd, $pipes = NULL, $env = NULL, $persist = false) {
  105. if (is_null($pipes))
  106. $pipes = array(0=>'r', 1=>'w', 2=>'w');
  107. $packet = array(
  108. 'cmd' => $cmd,
  109. 'pipes' => $pipes,
  110. );
  111. if ($persist !== false)
  112. $packet['persist'] = $persist;
  113. if (!is_null($env)) $packet['env'] = $env;
  114. $this->sendPacket(serialize($packet), self::PKT_RUN);
  115. $pid = $this->readPacket();
  116. if ($pid === '0') return false;
  117. list(,$pid, $resume) = unpack('N2', $pid);
  118. $this->running[$pid] = true;
  119. $this->last_pid = $pid;
  120. $this->pipes[$pid] = $pipes;
  121. $this->pipes_buf[$pid] = array();
  122. if ($persist !== false) $this->persist[$pid] = true;
  123. return new NetBatch_Process($this, $pid, $resume, $persist);
  124. }
  125. public function dump($pid = NULL) {
  126. if (is_null($pid)) $pid = $this->last_pid;
  127. var_dump($this->pipes_buf[$pid]);
  128. }
  129. public function eof($fd, $pid = NULL) {
  130. if (is_null($pid)) $pid = $this->last_pid;
  131. // still has a buffer => not EOF
  132. if (isset($this->pipes_buf[$pid][$fd])) return false;
  133. // if the stream exists it's not EOF yet
  134. return !isset($this->pipes[$pid][$fd]);
  135. }
  136. public function read($fd, $size, $pid = NULL, $blocking) {
  137. if (is_null($pid))
  138. $pid = $this->last_pid;
  139. while(1) {
  140. if (isset($this->pipes_buf[$pid][$fd])) {
  141. if (strlen($this->pipes_buf[$pid][$fd]) >= $size) {
  142. $ret = substr($this->pipes_buf[$pid][$fd], 0, $size);
  143. if ($size == strlen($this->pipes_buf[$pid][$fd])) {
  144. unset($this->pipes_buf[$pid][$fd]);
  145. } else {
  146. $this->pipes_buf[$pid][$fd] = substr($this->pipes_buf[$pid][$fd], $size);
  147. }
  148. return $ret;
  149. }
  150. if (!isset($this->pipes[$pid][$fd])) {
  151. // reached EOF, flush buffer first
  152. $res = $this->pipes_buf[$pid][$fd];
  153. unset($this->pipes_buf[$pid][$fd]);
  154. return $res;
  155. }
  156. }
  157. if (!isset($this->pipes[$pid][$fd])) return false;
  158. if (!$this->getEvent($pid, $blocking)) break;
  159. }
  160. return false;
  161. }
  162. public function gets($fd, $size = NULL, $pid = NULL, $blocking) {
  163. if (is_null($pid))
  164. $pid = $this->last_pid;
  165. while(1) {
  166. if (isset($this->pipes_buf[$pid][$fd])) {
  167. $pos = strpos($this->pipes_buf[$pid][$fd], "\n");
  168. if ($pos !== false) {
  169. $pos++;
  170. $ret = substr($this->pipes_buf[$pid][$fd], 0, $pos);
  171. if ($pos == strlen($this->pipes_buf[$pid][$fd])) {
  172. unset($this->pipes_buf[$pid][$fd]);
  173. } else {
  174. $this->pipes_buf[$pid][$fd] = substr($this->pipes_buf[$pid][$fd], $pos);
  175. }
  176. return $ret;
  177. }
  178. if ((!is_null($size)) && (strlen($this->pipes_buf[$pid][$fd]) >= $size)) {
  179. $ret = substr($this->pipes_buf[$pid][$fd], 0, $size);
  180. if ($size == strlen($this->pipes_buf[$pid][$fd])) {
  181. unset($this->pipes_buf[$pid][$fd]);
  182. } else {
  183. $this->pipes_buf[$pid][$fd] = substr($this->pipes_buf[$pid][$fd], $size);
  184. }
  185. return $ret;
  186. }
  187. if (!isset($this->pipes[$pid][$fd])) {
  188. // reached EOF, flush buffer first
  189. $res = $this->pipes_buf[$pid][$fd];
  190. unset($this->pipes_buf[$pid][$fd]);
  191. return $res;
  192. }
  193. }
  194. if (!isset($this->pipes[$pid][$fd])) return false;
  195. if (!$this->getEvent($pid, $blocking)) break;
  196. }
  197. return false;
  198. }
  199. public function wait($pid = NULL) {
  200. if (is_null($pid)) $pid = $this->last_pid;
  201. while($this->running[$pid])
  202. $this->getEvent($pid);
  203. return $this->returnCode[$pid];
  204. }
  205. public function getExitCode($pid) {
  206. return $this->returnCode[$pid];
  207. }
  208. public function isRunning($pid) {
  209. return isset($this->running[$pid]);
  210. }
  211. public function hasProcesses() {
  212. return (bool)count($this->running);
  213. }
  214. public function write($fd, $data, $pid = NULL) {
  215. if (is_null($pid)) $pid = $this->last_pid;
  216. $this->sendPacket(pack('NN', $pid, $fd).$data, self::PKT_DATA);
  217. }
  218. public function kill($signal = 15, $pid = NULL) {
  219. if (is_null($pid)) $pid = $this->last_pid;
  220. $this->sendPacket(pack('NN', $pid, $signal), self::PKT_KILL);
  221. }
  222. public function poll($pid) {
  223. $this->sendPacket(pack('N', $pid), self::PKT_POLL);
  224. }
  225. protected function getEvent($pid, $blocking = true) {
  226. $pkt = $this->readPacket($pid, $blocking);
  227. if ($pkt === false) return false;
  228. switch($this->type) {
  229. case self::PKT_EOF:
  230. list(,$pid, $fd) = unpack('N2', $pkt);
  231. unset($this->pipes[$pid][$fd]);
  232. break;
  233. case self::PKT_DATA:
  234. list(,$pid,$fd) = unpack('N2', substr($pkt, 0, 8));
  235. $pkt = (string)substr($pkt, 8);
  236. $this->pipes_buf[$pid][$fd] .= $pkt;
  237. break;
  238. case self::PKT_NOPIPES:
  239. // mmh?
  240. break;
  241. case self::PKT_RETURNCODE:
  242. list(,$pid, $rc) = unpack('N2', $pkt);
  243. unset($this->running[$pid]);
  244. unset($this->pipes[$pid]);
  245. $this->returnCode[$pid] = $rc;
  246. break;
  247. default:
  248. var_dump($this->type);
  249. break;
  250. }
  251. return true;
  252. }
  253. public function getActive(array $tmp) {
  254. if (!$tmp)
  255. throw new Exception('getActive() called without params');
  256. $this->getEvent(0, false);
  257. $list = array();
  258. $keys = array();
  259. foreach($tmp as $key => $process) {
  260. $list[$process->getPid()] = $process;
  261. $keys[$process->getPid()] = $key;
  262. }
  263. $final_list = array();
  264. while(!$final_list) {
  265. foreach($list as $pid => $process) {
  266. if ($this->pipes_buf[$pid]) {
  267. $final_list[$keys[$pid]] = $process;
  268. }
  269. if (!$process->running())
  270. $final_list[$keys[$pid]] = $process;
  271. }
  272. if (!$final_list)
  273. $this->getEvent(0);
  274. }
  275. return $final_list;
  276. }
  277. public function close($fd, $pid = NULL) {
  278. if (is_null($pid)) $pid = $this->last_pid;
  279. $this->sendPacket(pack('NN', $pid, $fd), self::PKT_CLOSE);
  280. }
  281. protected function sendPacket($data, $type = self::PKT_STANDARD) {
  282. return fwrite($this->fp, pack('nn', $type, strlen($data)).$data);
  283. }
  284. protected function readPacket($pid = 0, $blocking = true) {
  285. if (feof($this->fp)) throw new Exception('Connection lost');
  286. if ($this->persist[$pid]) {
  287. $now = time();
  288. if ($this->persist[$pid] != $now) {
  289. $this->poll($pid);
  290. $this->persist[$pid] = $now;
  291. }
  292. if ($blocking)
  293. while (!stream_select($r = array($this->fp), $w = NULL, $e = NULL, 1)) {
  294. $this->poll($pid);
  295. }
  296. }
  297. if (!$blocking) {
  298. if (!stream_select($r = array($this->fp), $w = NULL, $e = NULL, 0))
  299. return false;
  300. }
  301. $len = fread($this->fp, 4);
  302. if (strlen($len) != 4) throw new Exception('Connection lost');
  303. list(,$type,$len) = unpack('n2', $len);
  304. $this->type = $type;
  305. if ($len == 0) return '';
  306. return fread($this->fp, $len);
  307. }
  308. }
  309. $netbatch = new NetBatch('127.0.0.1');
  310. $netbatch->ident('test','test');
  311. $process = $netbatch->run(array('php'), NULL, NULL, 'PhpEval');
  312. if (!$process->isResumed()) {
  313. $process->write(0, '<?php while(1) { echo "BLAH\n"; sleep(1); }');
  314. $process->close(0);
  315. }
  316. $c = 10;
  317. while(!$process->eof(1)) {
  318. var_dump(rtrim($process->gets(1)));
  319. if ($c-- < 0) {
  320. $process->kill();
  321. $c = 99;
  322. }
  323. }
  324. while(!$process->eof(2))
  325. var_dump(rtrim($process->gets(2)));
  326. /*
  327. $ping = array();
  328. $ping[] = $netbatch->run(array('ping', '127.0.0.1', '-c', '5'));
  329. $ping[] = $netbatch->run(array('ping', 'google.fr', '-c', '5'));
  330. foreach($ping as $process)
  331. $process->setBlocking(false);
  332. while($netbatch->hasProcesses()) {
  333. foreach($netbatch->getActive($ping) as $key => $process) {
  334. $line = $process->gets(1);
  335. while($line !== false) {
  336. echo 'PID#'.$process->getPid().': '.rtrim($line)."\n";
  337. $line = $process->gets(1);
  338. }
  339. if (!$process->running()) {
  340. echo 'PID#'.$process->getPid().' has exited with exit code '.$process->exitCode()."\n";
  341. unset($ping[$key]);
  342. }
  343. }
  344. }
  345. */
  346. /*
  347. echo "Running: php\n";
  348. $process = $netbatch->run(array('php'), NULL, array('foo' => 'bougaga!'));
  349. $process->write(0, '<?php print_r($_ENV);');
  350. $process->close(0); // close stdin
  351. while(!$process->eof(1))
  352. var_dump(rtrim($process->gets(1)));
  353. $process->wait();
  354. $process->dump();
  355. */