PageRenderTime 75ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://github.com/blekkzor/pinetd2
PHP | 309 lines | 244 code | 57 blank | 8 comment | 33 complexity | 95cb1f05d731098dd321186e6ef6e8e6 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 Client extends \pinetd\TCP\Client {
  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. const PKT_POLL = 10;
  17. private $salt = '';
  18. private $login = NULL;
  19. private $procs = array();
  20. public function welcomeUser() {
  21. $this->setMsgEnd("\r\n");
  22. return true;
  23. }
  24. function sendBanner() {
  25. $name = $this->IPC->getName();
  26. $this->sendMsg($name);
  27. $this->salt = '';
  28. $saltlen = mt_rand(36,70);
  29. for($i = 0; $i < $saltlen; $i++)
  30. $this->salt .= chr(mt_rand(0,255));
  31. $this->sendMsg($this->salt);
  32. }
  33. public function childSignaled($pid, $status, $signal = NULL) {
  34. if (is_null($this->procs)) return false; // end of process
  35. // ok, a process exited!
  36. if (!isset($this->procs[$pid])) return true;
  37. proc_close($this->procs[$pid]['proc']);
  38. unset($this->procs[$pid]);
  39. $this->sendMsg(pack('NN', $pid, $status).((string)$signal), self::PKT_RETURNCODE);
  40. return true;
  41. }
  42. public function shutdown() {
  43. if ($this->procs) {
  44. foreach($this->procs as $proc)
  45. proc_close($proc['proc']);
  46. }
  47. $this->procs = NULL;
  48. }
  49. protected function handleRun(array $pkt) {
  50. if (isset($pkt['persist'])) {
  51. // ok, let's transmit this to the persist engine
  52. $res = $this->IPC->callPort('NetBatch::Persist', 'run', array($pkt, $this->login));
  53. if (!$res) {
  54. $this->sendMsg('0');
  55. return;
  56. }
  57. $this->sendRawMsg($res);
  58. return;
  59. }
  60. $cmd = $pkt['cmd'];
  61. $pipestmp = $pkt['pipes'];
  62. $cwd = $this->login['cwd'];
  63. $env = $pkt['env']?:array();
  64. $pipes = array();
  65. // cleanup $cmd if needed
  66. if (!is_array($cmd)) {
  67. $cmd = explode(' ',$cmd);
  68. foreach($cmd as &$val) {
  69. if ($val[0] == '\'')
  70. $val = stripslashes(substr($val, 1, -1));
  71. }
  72. unset($val);
  73. }
  74. $final_cmd = '';
  75. foreach($cmd as $val) {
  76. if (($final_cmd == '') && ($this->login['run_limit'])) {
  77. if (!preg_match($this->login['run_limit'], $val)) {
  78. $this->sendMsg('0');
  79. return;
  80. }
  81. }
  82. $final_cmd .= escapeshellarg($val).' ';
  83. }
  84. foreach($pipestmp as $fd => $type) {
  85. $pipes[$fd] = array('pipe', $type);
  86. }
  87. Logger::log(Logger::LOG_INFO, 'Executing: '.$final_cmd);
  88. $fpipes = array();
  89. $proc = proc_open($final_cmd, $pipes, $fpipes, $cwd, $env, array('binary_pipes' => true));
  90. $pipes = $fpipes;
  91. if (!is_resource($proc)) {
  92. $this->sendMsg('0');
  93. return;
  94. }
  95. $status = proc_get_status($proc);
  96. $this->sendMsg(pack('NN', $status['pid'], 0));
  97. if (!$status['running']) {
  98. // wtf? already died?
  99. $this->sendMsg(pack('N', $status['pid']), self::PKT_NOPIPES);
  100. $this->sendMsg(pack('NN', $status['pid'], $status['exitcode']), self::PKT_RETURNCODE);
  101. foreach($pipes as $fd)
  102. fclose($fd);
  103. return;
  104. }
  105. $this->procs[$status['pid']] = array('proc' => $proc, 'pipes' => $pipes);
  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. }
  112. protected function checkPipes($pid) {
  113. if ($this->procs[$pid]['pipes']) return;
  114. // NO MOAR PIPES!
  115. $this->sendMsg(pack('N', $pid), self::PKT_NOPIPES);
  116. // check if execution completed
  117. $status = proc_get_status($this->procs[$pid]['proc']);
  118. if (!$status['running']) {
  119. proc_close($this->procs[$pid]['proc']);
  120. unset($this->procs[$pid]);
  121. $this->sendMsg(pack('NN', $pid, $status['exitcode']), self::PKT_RETURNCODE);
  122. }
  123. }
  124. protected function handleWriteData($data) {
  125. $odata = $data;
  126. list(,$pid,$fd) = unpack('N2', $data);
  127. $data = substr($data, 8);
  128. if (!isset($this->procs[$pid]['pipes'][$fd]))
  129. return $this->proxy($pid, 'handleWriteData', array($odata));
  130. fwrite($this->procs[$pid]['pipes'][$fd], $data);
  131. fflush($this->procs[$pid]['pipes'][$fd]);
  132. }
  133. public function handleNewData($pipe, $pid, $fd) {
  134. if (feof($fd)) {
  135. $this->sendMsg(pack('NN', $pid, $pipe), self::PKT_EOF);
  136. $this->IPC->removeSocket($fd);
  137. unset($this->procs[$pid]['pipes'][$pipe]);
  138. fclose($fd);
  139. $this->checkPipes($pid);
  140. return;
  141. }
  142. $data = fread($fd, 4096);
  143. if ($data === false) {
  144. $this->sendMsg(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. return;
  150. }
  151. $this->sendMsg(pack('NN', $pid, $pipe).$data, self::PKT_DATA);
  152. }
  153. protected function proxy($pid, $func, array $args) {
  154. $res = $this->IPC->callPort('NetBatch::Persist', 'proxy', array($pid, $this->login, $func, $args));
  155. $this->sendRawMsg($res);
  156. }
  157. protected function handleClose($buf) {
  158. list(, $pid, $fd) = unpack('N2', $buf);
  159. if (!isset($this->procs[$pid]['pipes'][$fd]))
  160. return $this->proxy($pid, 'handleClose', array($buf));
  161. $pipe = $this->procs[$pid]['pipes'][$fd];
  162. $this->sendMsg(pack('NN', $pid, $fd), self::PKT_EOF);
  163. $this->IPC->removeSocket($pipe);
  164. fclose($pipe);
  165. unset($this->procs[$pid]['pipes'][$fd]);
  166. }
  167. protected function handleLogin($buffer) {
  168. $login = substr($buffer, 20);
  169. $key = substr($buffer, 0, 20);
  170. $peer = $this->IPC->getRemotePeer($login);
  171. if (!$peer) {
  172. $this->sendMsg('0');
  173. return;
  174. }
  175. if (sha1($peer['key'].$this->salt, true) != $key) {
  176. $this->sendMsg('0');
  177. return;
  178. }
  179. $this->login = $peer;
  180. $this->sendMsg('1');
  181. Logger::log(Logger::LOG_INFO, 'User '.$peer['login'].' logged in');
  182. $suid = new \pinetd\SUID($peer['uid'], $peer['gid']);
  183. $suid->setIt();
  184. }
  185. protected function handleBuffer($buffer, $type) {
  186. if ($type == self::PKT_LOGIN) {
  187. $this->handleLogin($buffer);
  188. return;
  189. }
  190. if (is_null($this->login)) {
  191. $this->close();
  192. break;
  193. }
  194. switch($type) {
  195. case self::PKT_EXIT:
  196. $this->close();
  197. break;
  198. case self::PKT_RUN:
  199. $this->handleRun(unserialize($buffer));
  200. break;
  201. case self::PKT_CLOSE:
  202. $this->handleClose($buffer);
  203. break;
  204. case self::PKT_DATA:
  205. $this->handleWriteData($buffer);
  206. break;
  207. case self::PKT_POLL:
  208. list(,$pid) = unpack('N', $buffer);
  209. $res = $this->IPC->callPort('NetBatch::Persist', 'poll', array($pid, $this->login));
  210. $this->sendRawMsg($res);
  211. break;
  212. case self::PKT_KILL:
  213. list(,$pid,$signal) = unpack('N2', $buffer);
  214. if ($this->proc[$pid])
  215. proc_terminate($this->proc[$pid], $signal);
  216. else
  217. $this->proxy($pid, 'kill', array($pid, $signal));
  218. break;
  219. default:
  220. // TODO: log+error
  221. $this->close();
  222. break;
  223. }
  224. }
  225. protected function parseBuffer() {
  226. while($this->ok) {
  227. if (strlen($this->buf) < 4)
  228. break;
  229. list(,$type,$len) = unpack('n2', $this->buf);
  230. if ($type == self::PKT_EXIT) {
  231. // "exit"
  232. $this->close();
  233. break;
  234. }
  235. if (strlen($this->buf) < ($len+4)) {
  236. break;
  237. }
  238. $tmp = substr($this->buf, 4, $len);
  239. $this->buf = (string)substr($this->buf, $len+4);
  240. $this->handleBuffer($tmp, $type);
  241. }
  242. }
  243. public function sendRawMsg($msg) {
  244. fwrite($this->fd, $msg);
  245. }
  246. public function sendMsg($msg, $type = self::PKT_STANDARD) {
  247. if (!$this->ok) return false;
  248. $n = fwrite($this->fd, pack('nn', $type, strlen($msg)) . $msg);
  249. fflush($this->fd);
  250. return $n;
  251. }
  252. }