Supervisor.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * Author: 芸众商城 www.yunzshop.com
  5. * Date: 22/02/2017
  6. * Time: 18:48
  7. */
  8. namespace app\backend\modules\supervisord\services;
  9. use app\common\exceptions\ShopException;
  10. use PhpXmlRpc\Value;
  11. use PhpXmlRpc\Request;
  12. use PhpXmlRpc\Client;
  13. class Supervisor
  14. {
  15. const CHUNK_SIZE = 8192;
  16. protected $_hostname;
  17. protected $_port;
  18. protected $_timeout;
  19. protected $_username;
  20. protected $_password;
  21. protected $_socket;
  22. protected $_current_hostname;
  23. protected $_host_num;
  24. /**
  25. * Construct a supervisor client instance.
  26. * These parameters are handed over to fsockopen() so refer to its documentation for further details.
  27. *
  28. * @param string $hostname The hostname.
  29. * @param int $port The port number.
  30. */
  31. public function __construct($hostname, $port = -1,$_host_num = 1)
  32. {
  33. $this->_hostname = $hostname;
  34. $this->_port = $port;
  35. $this->_username = null;
  36. $this->_password = null;
  37. $this->_current_hostname = null;
  38. $this->_host_num = $_host_num;
  39. $this->setTimeout(null);
  40. }
  41. /**
  42. * Set the username and password.
  43. *
  44. * @param string $username The username.
  45. * @param string $password The password.
  46. */
  47. public function setAuth($username, $password) {
  48. $this->_username = $username;
  49. $this->_password = $password;
  50. }
  51. /**
  52. * Set the connection timeout.
  53. *
  54. * @param float $timeout The connection timeout, in seconds.
  55. */
  56. public function setTimeout($timeout) {
  57. $this->_timeout = is_null($timeout) ? ini_get("default_socket_timeout") : $timeout;
  58. }
  59. /**
  60. * @param null $hostname
  61. * @author: Merlin
  62. * @Time: 2020/9/23 18:06
  63. */
  64. public function setCurrentHostname($hostname = null){
  65. $this->_current_hostname = $hostname;
  66. }
  67. /**
  68. * Return the version of the RPC API used by supervisord
  69. *
  70. * This API is versioned separately from Supervisor itself. The API version returned by getAPIVersion only changes
  71. * when the API changes. Its purpose is to help the client identify with which version of the Supervisor API it
  72. * is communicating.
  73. *
  74. * When writing software that communicates with this API, it is highly recommended that you first test the
  75. * API version for compatibility before making method calls.
  76. *
  77. * @return string
  78. */
  79. public function getAPIVersion()
  80. {
  81. return $this->_rpcCall('supervisor', 'getAPIVersion');
  82. }
  83. /**
  84. * Return the version of the supervisor package in use by supervisord
  85. *
  86. * @return string
  87. */
  88. public function getSupervisorVersion()
  89. {
  90. return $this->_rpcCall('supervisor', 'getSupervisorVersion');
  91. }
  92. /**
  93. * Return identifying string of supervisord
  94. *
  95. * This method allows the client to identify with which Supervisor instance it is communicating in the case of
  96. * environments where multiple Supervisors may be running.
  97. *
  98. * The identification is a string that must be set in Supervisor's configuration file. This method simply returns
  99. * that value back to the client.
  100. *
  101. * @return string
  102. */
  103. public function getIdentification()
  104. {
  105. return $this->_rpcCall('supervisor', 'getIdentification');
  106. }
  107. /**
  108. * Return current state of supervisord as a struct
  109. *
  110. * This is an internal value maintained by Supervisor that determines what Supervisor believes to be its current
  111. * operational state.
  112. *
  113. * Some method calls can alter the current state of the Supervisor. For example, calling the method
  114. * supervisor.shutdown() while the station is in the RUNNING state places the Supervisor in the SHUTDOWN state
  115. * while it is shutting down.
  116. *
  117. * The supervisor.getState() method provides a means for the client to check Supervisor's state, both for
  118. * informational purposes and to ensure that the methods it intends to call will be permitted.
  119. *
  120. * array['statecode'] int State code
  121. * array['statename'] stirng State name
  122. *
  123. * @return array (see above)
  124. */
  125. public function getState()
  126. {
  127. return $this->_rpcCall('supervisor', 'getState');
  128. }
  129. /**
  130. * Return the PID of supervisord
  131. *
  132. * @return int
  133. */
  134. public function getPID()
  135. {
  136. return $this->_rpcCall('supervisor', 'getPID');
  137. }
  138. /**
  139. * Read length bytes from the main log starting at offset
  140. *
  141. * It can either return the entire log, a number of characters from the tail of the log, or a slice of the log
  142. * specified by the offset and length parameters:
  143. *
  144. * @param int $offset Offset to start reading from
  145. * @param int $length Number of bytes to read from the log
  146. * @return string
  147. */
  148. public function readLog($offset, $length = 0)
  149. {
  150. return $this->_rpcCall('supervisor', 'readLog', array(new Value($offset), new Value($length)));
  151. }
  152. /**
  153. * Clear the main log.
  154. *
  155. * If the log cannot be cleared because the log file does not exist, the fault NO_FILE will be raised. If the log
  156. * cannot be cleared for any other reason, the fault FAILED will be raised.
  157. *
  158. * @return boolean Result always returns true unless error
  159. */
  160. public function clearLog()
  161. {
  162. return $this->_rpcCall('supervisor', 'clearLog');
  163. }
  164. /**
  165. * Shut down the supervisor process
  166. *
  167. * This method shuts down the Supervisor daemon. If any processes are running, they are automatically killed
  168. * without warning.
  169. *
  170. * Unlike most other methods, if Supervisor is in the FATAL state, this method will still function.
  171. *
  172. * @return boolean Result always returns true unless error
  173. */
  174. public function shutdown()
  175. {
  176. return $this->_rpcCall('supervisor', 'shutdown');
  177. }
  178. /**
  179. * Restart the supervisor process
  180. *
  181. * This method soft restarts the Supervisor daemon. If any processes are running, they are automatically killed
  182. * without warning. Note that the actual UNIX process for Supervisor cannot restart; only Supervisor's main
  183. * program loop. This has the effect of resetting the internal states of Supervisor.
  184. *
  185. * Unlike most other methods, if Supervisor is in the FATAL state, this method will still function.
  186. *
  187. * @return boolean Result always returns true unless error
  188. */
  189. public function restart()
  190. {
  191. return $this->_rpcCall('supervisor', 'restart');
  192. }
  193. /**
  194. * Reload the supervisor configuration
  195. *
  196. * @return boolean Result always returns true unless error
  197. */
  198. public function reloadConfig()
  199. {
  200. return $this->_rpcCall('supervisor', 'reloadConfig');
  201. }
  202. /**
  203. * Get info about all available process configurations.
  204. *
  205. * Each struct represents a single process (i.e. groups get flattened).
  206. *
  207. * array[process]
  208. * ['group'] string Name of the process' group
  209. * ['name'] string Name of the process
  210. * ['inuse'] bool
  211. * ['autostart'] bool
  212. * ['process_prio'] int
  213. * ['group_prio'] int
  214. *
  215. * @return array
  216. */
  217. public function getAllConfigInfo()
  218. {
  219. return $this->_rpcCall('supervisor', 'getAllConfigInfo');
  220. }
  221. /**
  222. * Get info about a process named name
  223. *
  224. * array['name'] string Name of the process
  225. * array['group'] string Name of the process' group
  226. * array['strart'] int UNIX timestamp of when the process was started
  227. * array['stop'] int UNIX timestamp of when the process last ended, or 0 if the process has never been stopped
  228. * array['now'] int UNIX timestamp of the current time, which can be used to calculate process up-time
  229. * array['state'] int State code
  230. * array['statename'] string Description of state
  231. * array['stdout_logfile'] string Absolute path and filename to the STDOUT logfile
  232. * array['stderr_logfile'] string Absolute path and filename to the STDOUT logfile
  233. * array['spawnerr'] string Description of error that occurred during spawn, or empty string if none.
  234. * array['exitstatus'] int Exit status (errorlevel) of process, or 0 if the process is still running.
  235. * array['pid'] int UNIX process ID (PID) of the process, or 0 if the process is not running.
  236. *
  237. * @param string $processName The name of the process (or 'group:name')
  238. * @return array (see above)
  239. */
  240. public function getProcessInfo($processName)
  241. {
  242. return $this->_rpcCall('supervisor', 'getProcessInfo', $processName);
  243. }
  244. /**
  245. * Get info about all processes
  246. *
  247. * Each element contains a struct, and this struct contains the exact same elements as the struct returned by
  248. * getProcessInfo. If the process table is empty, an empty array is returned.
  249. *
  250. * array[process] array all processes information
  251. * [getProcessInfo] array {@Link getProcessInfo}
  252. *
  253. * @return array (see above)
  254. */
  255. public function getAllProcessInfo()
  256. {
  257. return $this->_rpcCall('supervisor', 'getAllProcessInfo');
  258. }
  259. /**
  260. * Start all processes listed in the configuration file
  261. *
  262. * @param bool $wait Wait for process to be fully started
  263. * @return boolean Result always true unless error
  264. */
  265. public function startAllProcesses($wait = true)
  266. {
  267. return $this->_rpcCall('supervisor', 'startAllProcesses', $wait);
  268. }
  269. /**
  270. * Start a process
  271. *
  272. * @param string $processName Process name (or group:name, or group:*)
  273. * @param bool $wait Wait for process to be fully started
  274. * @return boolean Result always true unless error
  275. */
  276. public function startProcess($processName, $wait = true)
  277. {
  278. return $this->_rpcCall('supervisor', 'startProcess', array(new Value($processName)));
  279. }
  280. /**
  281. * Start all processes in the group named 'name'
  282. *
  283. * @param string $groupName The group name
  284. * @param bool $wait Wait for process to be fully started
  285. * @return boolean Result always true unless error
  286. */
  287. public function startProcessGroup($groupName, $wait = true)
  288. {
  289. return $this->_rpcCall('supervisor', 'startProcessGroup', array(new Value($groupName())));
  290. }
  291. /**
  292. * @param bool $wait Wait for process to be fully started
  293. * @return boolean Result always true unless error
  294. */
  295. public function stopAllProcesses($wait = true)
  296. {
  297. return $this->_rpcCall('supervisor', 'stopAllProcesses', $wait);
  298. }
  299. /**
  300. * @param string $processName Process name (or group:name, or group:*)
  301. * @param bool $wait Wait for process to be fully started
  302. * @return boolean Result always true unless error
  303. */
  304. public function stopProcess($processName, $wait = true)
  305. {
  306. return $this->_rpcCall('supervisor', 'stopProcess', array(new Value($processName)));
  307. }
  308. /**
  309. * Stop all processes in the group named 'name'
  310. *
  311. * @param string $groupName The group name
  312. * @param bool $wait Wait for process to be fully started
  313. * @return boolean Result always true unless error
  314. */
  315. public function stopProcessGroup($groupName, $wait = true)
  316. {
  317. return $this->_rpcCall('supervisor', 'stopProcessGroup', array(new Value($groupName)));
  318. }
  319. /**
  320. * Send a string of chars to the stdin of the process name. If non-7-bit data is sent (unicode), it is encoded to
  321. * utf-8 before being sent to the process' stdin. If chars is not a string or is not unicode, raise
  322. * INCORRECT_PARAMETERS. If the process is not running, raise NOT_RUNNING. If the process' stdin cannot accept
  323. * input (e.g. it was closed by the child process), raise NO_FILE.
  324. *
  325. * @param string $processName The process name to send to (or 'group:name')
  326. * @param string $chars The character data to send to the process
  327. * @return boolean Result always true unless error
  328. */
  329. public function sendProcessStdin($processName, $chars)
  330. {
  331. return $this->_rpcCall('supervisor', 'sendProcessStdin', array(new Value($processName)));
  332. }
  333. /**
  334. * Send an event that will be received by event listener subprocesses subscribing to the RemoteCommunicationEvent.
  335. *
  336. * @param string $eventType String for the 'type' key in the event header
  337. * @param string $eventData Data for the event body
  338. * @return boolean Result always true unless error
  339. */
  340. public function sendRemoteCommEvent($eventType, $eventData)
  341. {
  342. return $this->_rpcCall('supervisor', 'sendRemoteCommEvent', array(new Value($eventType), new Value($eventData)));
  343. }
  344. /**
  345. * Update the config for a running process from config file.
  346. *
  347. * @param string $processName Name name of process group to add
  348. * @return boolean result true if successful
  349. */
  350. public function addProcessGroup($processName)
  351. {
  352. return $this->_rpcCall('supervisor', 'addProcessGroup', $processName);
  353. }
  354. /**
  355. * Remove a stopped process from the active configuration.
  356. *
  357. * @param string $processName Name name of process group to remove
  358. * @return boolean result Indicates whether the removal was successful
  359. */
  360. public function removeProcessGroup($processName)
  361. {
  362. return $this->_rpcCall('supervisor', 'removeProcessGroup', $processName);
  363. }
  364. /**
  365. * Read length bytes from name's stdout log starting at offset
  366. *
  367. * @param string $processName The name of the process (or 'group:name')
  368. * @param int $offset Offset to start reading from.
  369. * @param int $length Number of bytes to read from the log.
  370. * @return string
  371. */
  372. public function readProcessStdoutLog($processName, $offset, $length)
  373. {
  374. return $this->_rpcCall('supervisor', 'readProcessStdoutLog', array($processName, $offset, $length));
  375. }
  376. /**
  377. * Read length bytes from name's stderr log starting at offset
  378. *
  379. * @param string $processName The name of the process (or 'group:name')
  380. * @param int $offset Offset to start reading from.
  381. * @param int $length Number of bytes to read from the log.
  382. * @return string
  383. */
  384. public function readProcessStderrLog($processName, $offset, $length)
  385. {
  386. return $this->_rpcCall('supervisor', 'readProcessStderrLog', array(new Value($processName), new Value($offset), new Value($length)));
  387. }
  388. /**
  389. * Provides a more efficient way to tail the (stdout) log than readProcessStdoutLog(). Use readProcessStdoutLog()
  390. * to read chunks and tailProcessStdoutLog() to tail.
  391. *
  392. * Requests (length) bytes from the (name)'s log, starting at (offset). If the total log size is greater than
  393. * (offset + length), the overflow flag is set and the (offset) is automatically increased to position the buffer
  394. * at the end of the log. If less than (length) bytes are available, the maximum number of available bytes will be
  395. * returned. (offset) returned is always the last offset in the log +1.
  396. *
  397. * @param string $processName The name of the process (or 'group:name')
  398. * @param int $offset Offset to start reading from.
  399. * @param int $length Number of bytes to read from the log.
  400. * @return string
  401. */
  402. public function tailProcessStdoutLog($processName, $offset, $length)
  403. {
  404. return $this->_rpcCall('supervisor', 'tailProcessStdoutLog', array(new Value($processName), new Value($offset), new Value($length)));
  405. }
  406. /**
  407. * Provides a more efficient way to tail the (stderr) log than readProcessStderrLog(). Use readProcessStderrLog()
  408. * to read chunks and tailProcessStderrLog() to tail.
  409. *
  410. * Requests (length) bytes from the (name)'s log, starting at (offset). If the total log size is greater than
  411. * (offset + length), the overflow flag is set and the (offset) is automatically increased to position the buffer
  412. * at the end of the log. If less than (length) bytes are available, the maximum number of available bytes will
  413. * be returned. (offset) returned is always the last offset in the log +1.
  414. *
  415. * @param string $processName The name of the process (or 'group:name')
  416. * @param int $offset Offset to start reading from.
  417. * @param int $length Number of bytes to read from the log.
  418. * @return string
  419. */
  420. public function tailProcessStderrLog($processName, $offset, $length)
  421. {
  422. return $this->_rpcCall('supervisor', 'tailProcessStderrLog', array($processName, $offset, $length));
  423. }
  424. /**
  425. * Clear the stdout and stderr logs for the named process and reopen them.
  426. *
  427. * @param string $processName The name of the process (or 'group:name')
  428. * @return boolean Always true unless error
  429. */
  430. public function clearProcessLogs($processName)
  431. {
  432. return $this->_rpcCall('supervisor', 'clearProcessLogs', new Value($processName));
  433. }
  434. public function isRun() {
  435. $state = $this->getState();
  436. if ($state->errno == 5) {
  437. return false;
  438. }
  439. return true;
  440. }
  441. /**
  442. * Clear all process log files
  443. *
  444. * @return array An array of process status info structs
  445. */
  446. public function clearAllProcessLogs()
  447. {
  448. return $this->_rpcCall('supervisor', 'clearAllProcessLogs');
  449. }
  450. /**
  451. * Return an array listing the available method names
  452. *
  453. * @return array An array of method names available (strings).
  454. */
  455. public function listMethods()
  456. {
  457. return $this->_rpcCall('system', 'listMethods');
  458. }
  459. /**
  460. * Return a string showing the method's documentation
  461. *
  462. * @param string $methodName The name of the method.
  463. * @return string The documentation for the method name.
  464. */
  465. public function methodHelp($methodName)
  466. {
  467. return $this->_rpcCall('system', 'methodHelp', $methodName);
  468. }
  469. /**
  470. * Return an array describing the method signature in the form [rtype, ptype, ptype...] where rtype is the return
  471. * data type of the method, and ptypes are the parameter data types that the method accepts in method argument order.
  472. *
  473. * @param string $methodSignature The name of the method.
  474. * @return array
  475. */
  476. public function methodSignature($methodSignature)
  477. {
  478. return $this->_rpcCall('system', 'methodSignature', $methodSignature);
  479. }
  480. /**
  481. * Process an array of calls, and return an array of results. Calls should be structs of the form
  482. * {'methodName': string, 'params': array}. Each result will either be a single-item array containing the result
  483. * value, or a struct of the form {'faultCode': int, 'faultString': string}. This is useful when you need to make
  484. * lots of small calls without lots of round trips.
  485. *
  486. * @param array $calls An array of call requests
  487. * @return array
  488. */
  489. public function multicall(array $calls)
  490. {
  491. return $this->_rpcCall('system', 'multicall', $calls);
  492. }
  493. // Methods added by the Twiddler RPC extension from
  494. // https://github.com/mnaberez/supervisor_twiddler
  495. /**
  496. * Checks if the Twiddler extension is installed and configured in supervisord.conf
  497. *
  498. * @return bool true if the extension is available, else false
  499. */
  500. public function isTwiddlerAvailable()
  501. {
  502. $methods = $this->listMethods();
  503. return in_array('twiddler.getAPIVersion', $methods);
  504. }
  505. /**
  506. * Return the version of the Twiddler API
  507. *
  508. * @return string
  509. */
  510. public function getTwiddlerAPIVersion()
  511. {
  512. return $this->_rpcCall('twiddler', 'getAPIVersion');
  513. }
  514. /**
  515. * Return the group names
  516. *
  517. * @return array
  518. */
  519. public function getGroupNames()
  520. {
  521. return $this->_rpcCall('twiddler', 'getGroupNames');
  522. }
  523. /**
  524. * Add a program to a group
  525. *
  526. * @param string $group The name of the group
  527. * @param string $program The name of the program
  528. * @param array $options Options
  529. * @return boolean
  530. */
  531. public function addProgramToGroup($group, $program, $options = array())
  532. {
  533. return $this->_rpcCall('twiddler', 'addProgramToGroup', array($group, $program, $options));
  534. }
  535. /**
  536. * Remove a process from a group
  537. *
  538. * @param string $group The name of the group
  539. * @param string $processName The name of the process
  540. * @return boolean
  541. */
  542. public function removeProcessFromGroup($group, $processName)
  543. {
  544. return $this->_rpcCall('twiddler', 'removeProcessFromGroup', array($group, $processName));
  545. }
  546. /**
  547. * Log a message to the supervisor log
  548. *
  549. * @param string $msg The message
  550. * @param string $level The level (CRIT, ERRO, WARN, INFO, DEBG, TRAC or BLAT)
  551. * @return boolean
  552. */
  553. public function logMessage($msg, $level = "INFO")
  554. {
  555. return $this->_rpcCall('twiddler', 'log', array($msg, $level));
  556. }
  557. /**
  558. * @param string $namespace The namespace of the request
  559. * @param string $method The method in the namespace
  560. * @param mixed $args Optional arguments
  561. * @return mixed
  562. * @throws \Exception
  563. */
  564. protected function _rpcCall($namespace, $method, $args = array())
  565. {
  566. if (!is_array($args)) {
  567. $args = array($args);
  568. }
  569. // Send the request to the supervisor XML-RPC API.
  570. return $this->_doRequest($namespace, $method, $args);
  571. }
  572. /**
  573. * Do a request to the supervisor XML-RPC API
  574. *
  575. * @param string $namespace The namespace of the request
  576. * @param string $method The method in the namespace
  577. * @param mixed $args Optional arguments
  578. */
  579. protected function _doRequest($namespace, $method, $args)
  580. {
  581. $result = [];
  582. $hostname = $this->_hostname;
  583. if ($this->_current_hostname) {
  584. $hostname = [$this->_current_hostname];
  585. }
  586. foreach ($hostname as $_hostname) {
  587. $client = new Client($_hostname . ":" . $this->_port . "/RPC2");
  588. $client->return_type = "phpvals";
  589. $response = $client->send(new Request("$namespace.$method", $args));
  590. $result[$_hostname] = $response;
  591. }
  592. return $result;
  593. }
  594. public function getHostname()
  595. {
  596. return $this->_hostname;
  597. }
  598. public function getHostNum()
  599. {
  600. return $this->_host_num;
  601. }
  602. /**
  603. * Close the socket when the class destructs
  604. */
  605. public function __destruct()
  606. {
  607. if (is_resource($this->_socket)) {
  608. fclose($this->_socket);
  609. }
  610. }
  611. }