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

/code/classes/Daemon/NetBatch/Persist_Client.class.php

https://github.com/blekkzor/pinetd2
PHP | 270 lines | 208 code | 51 blank | 11 comment | 29 complexity | 2426b0bbf808fb17f80a4499e5dbbd09 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. namespace Daemon\NetBatch;
  3. use pinetd\Logger;
  4. use pinetd\Timer;
  5. class Persist_Client extends \pinetd\ProcessChild {
  6. const PKT_STANDARD = 0;
  7. const PKT_LOGIN = 1;
  8. const PKT_EXIT = 2;
  9. const PKT_RUN = 3;
  10. const PKT_RETURNCODE = 4;
  11. const PKT_EOF = 5;
  12. const PKT_CLOSE = 6;
  13. const PKT_DATA = 7;
  14. const PKT_NOPIPES = 8;
  15. const PKT_KILL = 9;
  16. private $procs = array();
  17. private $queue = array();
  18. private $pkeys = array();
  19. private $msg = '';
  20. private $login;
  21. public function setLogin(array $login) {
  22. $this->login = $login;
  23. // drop privileges
  24. $suid = new \pinetd\SUID($login['uid'], $login['gid']);
  25. $suid->setIt();
  26. }
  27. public function childSignaled($pid, $status, $signal = NULL) {
  28. if (is_null($this->procs)) return false; // end of process
  29. // ok, a process exited!
  30. if (!isset($this->procs[$pid])) return true;
  31. // close all (remaining) fds for this process and call $this->checkPipes($pid)
  32. foreach($this->procs[$pid]['pipes'] as $id => $fd) {
  33. // Send EOF
  34. $this->addMsg(pack('NN', $pid, $id), self::PKT_EOF);
  35. fclose($fd);
  36. }
  37. $this->addMsg(pack('N', $pid), self::PKT_NOPIPES);
  38. $this->procs[$pid]['pipes'] = array();
  39. if ($this->procs[$pid]['proc'])
  40. proc_close($this->procs[$pid]['proc']);
  41. if ($this->procs[$pid]['pkey']) unset($this->pkeys[$this->procs[$pid]['pkey']]);
  42. unset($this->procs[$pid]);
  43. $this->addMsg(pack('NN', $pid, $status).((string)$signal), self::PKT_RETURNCODE);
  44. $this->fetchMsg($pid);
  45. return true;
  46. }
  47. public function _ParentIPC_doRun($null, array $pkt) {
  48. $cmd = $pkt['cmd'];
  49. $pipestmp = $pkt['pipes'];
  50. $cwd = $this->login['cwd'];
  51. $env = $pkt['env']?:array();
  52. $persist_key = $pkt['persist']?:false;
  53. if ((is_string($persist_key)) && (isset($this->pkeys[$persist_key]))) {
  54. $this->addMsg(pack('NN', $this->pkeys[$persist_key], 1));
  55. return $this->fetchMsg();
  56. }
  57. $pipes = array();
  58. // TODO: if persist, pass run request to process thread so the process will
  59. // persist even when connection is closed
  60. // cleanup $cmd if needed
  61. if (!is_array($cmd)) {
  62. $cmd = explode(' ',$cmd);
  63. foreach($cmd as &$val) {
  64. if ($val[0] == '\'')
  65. $val = stripslashes(substr($val, 1, -1));
  66. }
  67. unset($val);
  68. }
  69. $final_cmd = '';
  70. foreach($cmd as $val) {
  71. if (($final_cmd == '') && ($this->login['run_limit'])) {
  72. if (!preg_match($this->login['run_limit'], $val)) {
  73. $this->addMsg('0');
  74. return $this->fetchMsg();
  75. }
  76. }
  77. $final_cmd .= escapeshellarg($val).' ';
  78. }
  79. foreach($pipestmp as $fd => $type) {
  80. $pipes[$fd] = array('pipe', $type);
  81. }
  82. Logger::log(Logger::LOG_INFO, 'Executing: '.$final_cmd);
  83. $fpipes = array();
  84. $proc = proc_open($final_cmd, $pipes, $fpipes, $cwd, $env, array('binary_pipes' => true));
  85. $pipes = $fpipes;
  86. if (!is_resource($proc)) {
  87. $this->addMsg('0');
  88. return $this->fetchMsg();
  89. }
  90. $status = proc_get_status($proc);
  91. $this->addMsg(pack('NN', $status['pid'], 0));
  92. if (!$status['running']) {
  93. // wtf? already died?
  94. $this->addMsg(pack('N', $status['pid']), self::PKT_NOPIPES);
  95. $this->addMsg(pack('NN', $status['pid'], $status['exitcode']), self::PKT_RETURNCODE);
  96. foreach($pipes as $fd)
  97. fclose($fd);
  98. return $this->fetchMsg();
  99. }
  100. $insert = array('proc' => $proc, 'pipes' => $pipes);
  101. if (is_string($persist_key)) {
  102. $this->pkeys[$persist_key] = $status['pid'];
  103. $insert['pkey'] = $persist_key;
  104. }
  105. $this->procs[$status['pid']] = $insert;
  106. foreach($pipes as $id => $fd) {
  107. $extra = array($id, $status['pid'], $fd);
  108. $this->IPC->registerSocketWait($fd, array($this, 'handleNewData'), $extra);
  109. unset($extra);
  110. }
  111. return $this->fetchMsg();
  112. }
  113. protected function checkPipes($pid) {
  114. if ($this->procs[$pid]['pipes']) return;
  115. // NO MOAR PIPES!
  116. $this->addMsg(pack('N', $pid), self::PKT_NOPIPES);
  117. // check if execution completed
  118. if ($this->procs[$pid]['proc']) {
  119. $status = proc_get_status($this->procs[$pid]['proc']);
  120. } else {
  121. $status = array(
  122. 'running' => false,
  123. 'exitcode' => -1,
  124. );
  125. }
  126. if (!$status['running']) {
  127. if ($this->procs[$pid]['proc'])
  128. proc_close($this->procs[$pid]['proc']);
  129. if ($this->procs[$pid]['pkey']) unset($this->pkeys[$this->procs[$pid]['pkey']]);
  130. unset($this->procs[$pid]);
  131. $this->addMsg(pack('NN', $pid, $status['exitcode']), self::PKT_RETURNCODE);
  132. }
  133. $this->fetchMsg($pid);
  134. }
  135. public function _ParentIPC_handleWriteData($null, $data) {
  136. list(,$pid,$fd) = unpack('N2', $data);
  137. $data = substr($data, 8);
  138. if (!isset($this->procs[$pid]['pipes'][$fd])) return;
  139. fwrite($this->procs[$pid]['pipes'][$fd], $data);
  140. fflush($this->procs[$pid]['pipes'][$fd]);
  141. }
  142. public function handleNewData($pipe, $pid, $fd) {
  143. if (feof($fd)) {
  144. $this->addMsg(pack('NN', $pid, $pipe), self::PKT_EOF);
  145. $this->IPC->removeSocket($fd);
  146. unset($this->procs[$pid]['pipes'][$pipe]);
  147. fclose($fd);
  148. $this->checkPipes($pid);
  149. $this->fetchMsg($pid);
  150. return;
  151. }
  152. $data = fread($fd, 4096);
  153. if ($data === false) {
  154. $this->addMsg(pack('NN', $pid, $pipe), self::PKT_EOF);
  155. $this->IPC->removeSocket($fd);
  156. unset($this->procs[$pid]['pipes'][$pipe]);
  157. fclose($fd);
  158. $this->checkPipes($pid);
  159. $this->fetchMsg($pid);
  160. return;
  161. }
  162. $this->addMsg(pack('NN', $pid, $pipe).$data, self::PKT_DATA);
  163. $this->fetchMsg($pid);
  164. }
  165. public function _ParentIPC_kill($null, $pid, $signal) {
  166. if (isset($this->procs[$pid])) {
  167. proc_terminate($this->procs[$pid]['proc'], $signal);
  168. }
  169. }
  170. public function _ParentIPC_handleClose($null, $buf) {
  171. list(, $pid, $fd) = unpack('N2', $buf);
  172. if (!isset($this->procs[$pid]['pipes'][$fd])) return;
  173. $pipe = $this->procs[$pid]['pipes'][$fd];
  174. $this->addMsg(pack('NN', $pid, $fd), self::PKT_EOF);
  175. $this->IPC->removeSocket($pipe);
  176. fclose($pipe);
  177. unset($this->procs[$pid]['pipes'][$fd]);
  178. $this->fetchMsg($pid);
  179. }
  180. public function addMsg($msg, $type = self::PKT_STANDARD) {
  181. $this->msg .= pack('nn', $type, strlen($msg)) . $msg;
  182. }
  183. public function _ParentIPC_fetchMsg() {
  184. $ret = $this->fetchMsg();
  185. return $ret;
  186. }
  187. public function fetchMsg($pid = NULL) {
  188. $tmp = $this->msg;
  189. $this->msg = '';
  190. if (!is_null($pid)) {
  191. $this->queue[$pid] .= $tmp;
  192. return true;
  193. }
  194. return $tmp;
  195. }
  196. public function _ParentIPC_pollPid($null, $pid) {
  197. $tmp = $this->queue[$pid];
  198. unset($this->queue[$pid]);
  199. return $tmp;
  200. }
  201. public function mainLoop($IPC) {
  202. $this->IPC = $IPC;
  203. $this->IPC->setParent($this);
  204. // $this->localConfig = $this->IPC->getLocalConfig();
  205. $this->processBaseName = get_class($this).' for '.$this->login['login'];
  206. $this->setProcessStatus();
  207. while(1) {
  208. $this->IPC->selectSockets(200000);
  209. Timer::processTimers();
  210. }
  211. }
  212. public function shutdown() {
  213. if ($this->procs) {
  214. foreach($this->procs as $proc)
  215. proc_terminate($proc['proc'], 9);
  216. }
  217. $this->procs = NULL;
  218. }
  219. }