666 lines
22 KiB
PHP
666 lines
22 KiB
PHP
<?php
|
|
/**
|
|
* Created by PhpStorm.
|
|
* Author:
|
|
* Date: 22/02/2017
|
|
* Time: 18:48
|
|
*/
|
|
|
|
namespace app\backend\modules\supervisord\services;
|
|
|
|
use app\common\exceptions\ShopException;
|
|
use PhpXmlRpc\Value;
|
|
use PhpXmlRpc\Request;
|
|
use PhpXmlRpc\Client;
|
|
|
|
|
|
class Supervisor
|
|
{
|
|
const CHUNK_SIZE = 8192;
|
|
|
|
protected $_hostname;
|
|
protected $_port;
|
|
protected $_timeout;
|
|
protected $_username;
|
|
protected $_password;
|
|
protected $_socket;
|
|
protected $_current_hostname;
|
|
protected $_host_num;
|
|
|
|
/**
|
|
* Construct a supervisor client instance.
|
|
* These parameters are handed over to fsockopen() so refer to its documentation for further details.
|
|
*
|
|
* @param string $hostname The hostname.
|
|
* @param int $port The port number.
|
|
*/
|
|
public function __construct($hostname, $port = -1,$_host_num = 1)
|
|
{
|
|
$this->_hostname = $hostname;
|
|
$this->_port = $port;
|
|
$this->_username = null;
|
|
$this->_password = null;
|
|
$this->_current_hostname = null;
|
|
$this->_host_num = $_host_num;
|
|
$this->setTimeout(null);
|
|
}
|
|
|
|
/**
|
|
* Set the username and password.
|
|
*
|
|
* @param string $username The username.
|
|
* @param string $password The password.
|
|
*/
|
|
public function setAuth($username, $password) {
|
|
$this->_username = $username;
|
|
$this->_password = $password;
|
|
}
|
|
|
|
/**
|
|
* Set the connection timeout.
|
|
*
|
|
* @param float $timeout The connection timeout, in seconds.
|
|
*/
|
|
public function setTimeout($timeout) {
|
|
$this->_timeout = is_null($timeout) ? ini_get("default_socket_timeout") : $timeout;
|
|
}
|
|
|
|
/**
|
|
* @param null $hostname
|
|
* @author: Merlin
|
|
* @Time: 2020/9/23 18:06
|
|
*/
|
|
public function setCurrentHostname($hostname = null){
|
|
$this->_current_hostname = $hostname;
|
|
}
|
|
|
|
/**
|
|
* Return the version of the RPC API used by supervisord
|
|
*
|
|
* This API is versioned separately from Supervisor itself. The API version returned by getAPIVersion only changes
|
|
* when the API changes. Its purpose is to help the client identify with which version of the Supervisor API it
|
|
* is communicating.
|
|
*
|
|
* When writing software that communicates with this API, it is highly recommended that you first test the
|
|
* API version for compatibility before making method calls.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getAPIVersion()
|
|
{
|
|
return $this->_rpcCall('supervisor', 'getAPIVersion');
|
|
}
|
|
|
|
/**
|
|
* Return the version of the supervisor package in use by supervisord
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getSupervisorVersion()
|
|
{
|
|
return $this->_rpcCall('supervisor', 'getSupervisorVersion');
|
|
}
|
|
|
|
/**
|
|
* Return identifying string of supervisord
|
|
*
|
|
* This method allows the client to identify with which Supervisor instance it is communicating in the case of
|
|
* environments where multiple Supervisors may be running.
|
|
*
|
|
* The identification is a string that must be set in Supervisor's configuration file. This method simply returns
|
|
* that value back to the client.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getIdentification()
|
|
{
|
|
return $this->_rpcCall('supervisor', 'getIdentification');
|
|
}
|
|
|
|
/**
|
|
* Return current state of supervisord as a struct
|
|
*
|
|
* This is an internal value maintained by Supervisor that determines what Supervisor believes to be its current
|
|
* operational state.
|
|
*
|
|
* Some method calls can alter the current state of the Supervisor. For example, calling the method
|
|
* supervisor.shutdown() while the station is in the RUNNING state places the Supervisor in the SHUTDOWN state
|
|
* while it is shutting down.
|
|
*
|
|
* The supervisor.getState() method provides a means for the client to check Supervisor's state, both for
|
|
* informational purposes and to ensure that the methods it intends to call will be permitted.
|
|
*
|
|
* array['statecode'] int State code
|
|
* array['statename'] stirng State name
|
|
*
|
|
* @return array (see above)
|
|
*/
|
|
public function getState()
|
|
{
|
|
return $this->_rpcCall('supervisor', 'getState');
|
|
}
|
|
|
|
/**
|
|
* Return the PID of supervisord
|
|
*
|
|
* @return int
|
|
*/
|
|
public function getPID()
|
|
{
|
|
return $this->_rpcCall('supervisor', 'getPID');
|
|
}
|
|
|
|
/**
|
|
* Read length bytes from the main log starting at offset
|
|
*
|
|
* It can either return the entire log, a number of characters from the tail of the log, or a slice of the log
|
|
* specified by the offset and length parameters:
|
|
*
|
|
* @param int $offset Offset to start reading from
|
|
* @param int $length Number of bytes to read from the log
|
|
* @return string
|
|
*/
|
|
public function readLog($offset, $length = 0)
|
|
{
|
|
return $this->_rpcCall('supervisor', 'readLog', array(new Value($offset), new Value($length)));
|
|
}
|
|
|
|
/**
|
|
* Clear the main log.
|
|
*
|
|
* If the log cannot be cleared because the log file does not exist, the fault NO_FILE will be raised. If the log
|
|
* cannot be cleared for any other reason, the fault FAILED will be raised.
|
|
*
|
|
* @return boolean Result always returns true unless error
|
|
*/
|
|
public function clearLog()
|
|
{
|
|
return $this->_rpcCall('supervisor', 'clearLog');
|
|
}
|
|
|
|
/**
|
|
* Shut down the supervisor process
|
|
*
|
|
* This method shuts down the Supervisor daemon. If any processes are running, they are automatically killed
|
|
* without warning.
|
|
*
|
|
* Unlike most other methods, if Supervisor is in the FATAL state, this method will still function.
|
|
*
|
|
* @return boolean Result always returns true unless error
|
|
*/
|
|
public function shutdown()
|
|
{
|
|
return $this->_rpcCall('supervisor', 'shutdown');
|
|
}
|
|
|
|
/**
|
|
* Restart the supervisor process
|
|
*
|
|
* This method soft restarts the Supervisor daemon. If any processes are running, they are automatically killed
|
|
* without warning. Note that the actual UNIX process for Supervisor cannot restart; only Supervisor's main
|
|
* program loop. This has the effect of resetting the internal states of Supervisor.
|
|
*
|
|
* Unlike most other methods, if Supervisor is in the FATAL state, this method will still function.
|
|
*
|
|
* @return boolean Result always returns true unless error
|
|
*/
|
|
public function restart()
|
|
{
|
|
return $this->_rpcCall('supervisor', 'restart');
|
|
}
|
|
|
|
/**
|
|
* Reload the supervisor configuration
|
|
*
|
|
* @return boolean Result always returns true unless error
|
|
*/
|
|
public function reloadConfig()
|
|
{
|
|
return $this->_rpcCall('supervisor', 'reloadConfig');
|
|
}
|
|
|
|
/**
|
|
* Get info about all available process configurations.
|
|
*
|
|
* Each struct represents a single process (i.e. groups get flattened).
|
|
*
|
|
* array[process]
|
|
* ['group'] string Name of the process' group
|
|
* ['name'] string Name of the process
|
|
* ['inuse'] bool
|
|
* ['autostart'] bool
|
|
* ['process_prio'] int
|
|
* ['group_prio'] int
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getAllConfigInfo()
|
|
{
|
|
return $this->_rpcCall('supervisor', 'getAllConfigInfo');
|
|
}
|
|
|
|
/**
|
|
* Get info about a process named name
|
|
*
|
|
* array['name'] string Name of the process
|
|
* array['group'] string Name of the process' group
|
|
* array['strart'] int UNIX timestamp of when the process was started
|
|
* array['stop'] int UNIX timestamp of when the process last ended, or 0 if the process has never been stopped
|
|
* array['now'] int UNIX timestamp of the current time, which can be used to calculate process up-time
|
|
* array['state'] int State code
|
|
* array['statename'] string Description of state
|
|
* array['stdout_logfile'] string Absolute path and filename to the STDOUT logfile
|
|
* array['stderr_logfile'] string Absolute path and filename to the STDOUT logfile
|
|
* array['spawnerr'] string Description of error that occurred during spawn, or empty string if none.
|
|
* array['exitstatus'] int Exit status (errorlevel) of process, or 0 if the process is still running.
|
|
* array['pid'] int UNIX process ID (PID) of the process, or 0 if the process is not running.
|
|
*
|
|
* @param string $processName The name of the process (or 'group:name')
|
|
* @return array (see above)
|
|
*/
|
|
public function getProcessInfo($processName)
|
|
{
|
|
return $this->_rpcCall('supervisor', 'getProcessInfo', $processName);
|
|
}
|
|
|
|
/**
|
|
* Get info about all processes
|
|
*
|
|
* Each element contains a struct, and this struct contains the exact same elements as the struct returned by
|
|
* getProcessInfo. If the process table is empty, an empty array is returned.
|
|
*
|
|
* array[process] array all processes information
|
|
* [getProcessInfo] array {@Link getProcessInfo}
|
|
*
|
|
* @return array (see above)
|
|
*/
|
|
public function getAllProcessInfo()
|
|
{
|
|
return $this->_rpcCall('supervisor', 'getAllProcessInfo');
|
|
}
|
|
|
|
/**
|
|
* Start all processes listed in the configuration file
|
|
*
|
|
* @param bool $wait Wait for process to be fully started
|
|
* @return boolean Result always true unless error
|
|
*/
|
|
public function startAllProcesses($wait = true)
|
|
{
|
|
return $this->_rpcCall('supervisor', 'startAllProcesses', $wait);
|
|
}
|
|
|
|
/**
|
|
* Start a process
|
|
*
|
|
* @param string $processName Process name (or group:name, or group:*)
|
|
* @param bool $wait Wait for process to be fully started
|
|
* @return boolean Result always true unless error
|
|
*/
|
|
public function startProcess($processName, $wait = true)
|
|
{
|
|
return $this->_rpcCall('supervisor', 'startProcess', array(new Value($processName)));
|
|
}
|
|
|
|
/**
|
|
* Start all processes in the group named 'name'
|
|
*
|
|
* @param string $groupName The group name
|
|
* @param bool $wait Wait for process to be fully started
|
|
* @return boolean Result always true unless error
|
|
*/
|
|
public function startProcessGroup($groupName, $wait = true)
|
|
{
|
|
return $this->_rpcCall('supervisor', 'startProcessGroup', array(new Value($groupName())));
|
|
}
|
|
|
|
/**
|
|
* @param bool $wait Wait for process to be fully started
|
|
* @return boolean Result always true unless error
|
|
*/
|
|
public function stopAllProcesses($wait = true)
|
|
{
|
|
return $this->_rpcCall('supervisor', 'stopAllProcesses', $wait);
|
|
}
|
|
|
|
/**
|
|
* @param string $processName Process name (or group:name, or group:*)
|
|
* @param bool $wait Wait for process to be fully started
|
|
* @return boolean Result always true unless error
|
|
*/
|
|
public function stopProcess($processName, $wait = true)
|
|
{
|
|
return $this->_rpcCall('supervisor', 'stopProcess', array(new Value($processName)));
|
|
}
|
|
|
|
/**
|
|
* Stop all processes in the group named 'name'
|
|
*
|
|
* @param string $groupName The group name
|
|
* @param bool $wait Wait for process to be fully started
|
|
* @return boolean Result always true unless error
|
|
*/
|
|
public function stopProcessGroup($groupName, $wait = true)
|
|
{
|
|
return $this->_rpcCall('supervisor', 'stopProcessGroup', array(new Value($groupName)));
|
|
}
|
|
|
|
/**
|
|
* Send a string of chars to the stdin of the process name. If non-7-bit data is sent (unicode), it is encoded to
|
|
* utf-8 before being sent to the process' stdin. If chars is not a string or is not unicode, raise
|
|
* INCORRECT_PARAMETERS. If the process is not running, raise NOT_RUNNING. If the process' stdin cannot accept
|
|
* input (e.g. it was closed by the child process), raise NO_FILE.
|
|
*
|
|
* @param string $processName The process name to send to (or 'group:name')
|
|
* @param string $chars The character data to send to the process
|
|
* @return boolean Result always true unless error
|
|
*/
|
|
public function sendProcessStdin($processName, $chars)
|
|
{
|
|
return $this->_rpcCall('supervisor', 'sendProcessStdin', array(new Value($processName)));
|
|
}
|
|
|
|
/**
|
|
* Send an event that will be received by event listener subprocesses subscribing to the RemoteCommunicationEvent.
|
|
*
|
|
* @param string $eventType String for the 'type' key in the event header
|
|
* @param string $eventData Data for the event body
|
|
* @return boolean Result always true unless error
|
|
*/
|
|
public function sendRemoteCommEvent($eventType, $eventData)
|
|
{
|
|
return $this->_rpcCall('supervisor', 'sendRemoteCommEvent', array(new Value($eventType), new Value($eventData)));
|
|
}
|
|
|
|
/**
|
|
* Update the config for a running process from config file.
|
|
*
|
|
* @param string $processName Name name of process group to add
|
|
* @return boolean result true if successful
|
|
*/
|
|
public function addProcessGroup($processName)
|
|
{
|
|
return $this->_rpcCall('supervisor', 'addProcessGroup', $processName);
|
|
}
|
|
|
|
/**
|
|
* Remove a stopped process from the active configuration.
|
|
*
|
|
* @param string $processName Name name of process group to remove
|
|
* @return boolean result Indicates whether the removal was successful
|
|
*/
|
|
public function removeProcessGroup($processName)
|
|
{
|
|
return $this->_rpcCall('supervisor', 'removeProcessGroup', $processName);
|
|
}
|
|
|
|
/**
|
|
* Read length bytes from name's stdout log starting at offset
|
|
*
|
|
* @param string $processName The name of the process (or 'group:name')
|
|
* @param int $offset Offset to start reading from.
|
|
* @param int $length Number of bytes to read from the log.
|
|
* @return string
|
|
*/
|
|
public function readProcessStdoutLog($processName, $offset, $length)
|
|
{
|
|
return $this->_rpcCall('supervisor', 'readProcessStdoutLog', array($processName, $offset, $length));
|
|
}
|
|
|
|
/**
|
|
* Read length bytes from name's stderr log starting at offset
|
|
*
|
|
* @param string $processName The name of the process (or 'group:name')
|
|
* @param int $offset Offset to start reading from.
|
|
* @param int $length Number of bytes to read from the log.
|
|
* @return string
|
|
*/
|
|
public function readProcessStderrLog($processName, $offset, $length)
|
|
{
|
|
return $this->_rpcCall('supervisor', 'readProcessStderrLog', array(new Value($processName), new Value($offset), new Value($length)));
|
|
}
|
|
|
|
/**
|
|
* Provides a more efficient way to tail the (stdout) log than readProcessStdoutLog(). Use readProcessStdoutLog()
|
|
* to read chunks and tailProcessStdoutLog() to tail.
|
|
*
|
|
* Requests (length) bytes from the (name)'s log, starting at (offset). If the total log size is greater than
|
|
* (offset + length), the overflow flag is set and the (offset) is automatically increased to position the buffer
|
|
* at the end of the log. If less than (length) bytes are available, the maximum number of available bytes will be
|
|
* returned. (offset) returned is always the last offset in the log +1.
|
|
*
|
|
* @param string $processName The name of the process (or 'group:name')
|
|
* @param int $offset Offset to start reading from.
|
|
* @param int $length Number of bytes to read from the log.
|
|
* @return string
|
|
*/
|
|
public function tailProcessStdoutLog($processName, $offset, $length)
|
|
{
|
|
return $this->_rpcCall('supervisor', 'tailProcessStdoutLog', array(new Value($processName), new Value($offset), new Value($length)));
|
|
}
|
|
|
|
/**
|
|
* Provides a more efficient way to tail the (stderr) log than readProcessStderrLog(). Use readProcessStderrLog()
|
|
* to read chunks and tailProcessStderrLog() to tail.
|
|
*
|
|
* Requests (length) bytes from the (name)'s log, starting at (offset). If the total log size is greater than
|
|
* (offset + length), the overflow flag is set and the (offset) is automatically increased to position the buffer
|
|
* at the end of the log. If less than (length) bytes are available, the maximum number of available bytes will
|
|
* be returned. (offset) returned is always the last offset in the log +1.
|
|
*
|
|
* @param string $processName The name of the process (or 'group:name')
|
|
* @param int $offset Offset to start reading from.
|
|
* @param int $length Number of bytes to read from the log.
|
|
* @return string
|
|
*/
|
|
public function tailProcessStderrLog($processName, $offset, $length)
|
|
{
|
|
return $this->_rpcCall('supervisor', 'tailProcessStderrLog', array($processName, $offset, $length));
|
|
}
|
|
|
|
/**
|
|
* Clear the stdout and stderr logs for the named process and reopen them.
|
|
*
|
|
* @param string $processName The name of the process (or 'group:name')
|
|
* @return boolean Always true unless error
|
|
*/
|
|
public function clearProcessLogs($processName)
|
|
{
|
|
return $this->_rpcCall('supervisor', 'clearProcessLogs', new Value($processName));
|
|
}
|
|
|
|
public function isRun() {
|
|
$state = $this->getState();
|
|
if ($state->errno == 5) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Clear all process log files
|
|
*
|
|
* @return array An array of process status info structs
|
|
*/
|
|
public function clearAllProcessLogs()
|
|
{
|
|
return $this->_rpcCall('supervisor', 'clearAllProcessLogs');
|
|
}
|
|
|
|
/**
|
|
* Return an array listing the available method names
|
|
*
|
|
* @return array An array of method names available (strings).
|
|
*/
|
|
public function listMethods()
|
|
{
|
|
return $this->_rpcCall('system', 'listMethods');
|
|
}
|
|
|
|
/**
|
|
* Return a string showing the method's documentation
|
|
*
|
|
* @param string $methodName The name of the method.
|
|
* @return string The documentation for the method name.
|
|
*/
|
|
public function methodHelp($methodName)
|
|
{
|
|
return $this->_rpcCall('system', 'methodHelp', $methodName);
|
|
}
|
|
|
|
/**
|
|
* Return an array describing the method signature in the form [rtype, ptype, ptype...] where rtype is the return
|
|
* data type of the method, and ptypes are the parameter data types that the method accepts in method argument order.
|
|
*
|
|
* @param string $methodSignature The name of the method.
|
|
* @return array
|
|
*/
|
|
public function methodSignature($methodSignature)
|
|
{
|
|
return $this->_rpcCall('system', 'methodSignature', $methodSignature);
|
|
}
|
|
|
|
/**
|
|
* Process an array of calls, and return an array of results. Calls should be structs of the form
|
|
* {'methodName': string, 'params': array}. Each result will either be a single-item array containing the result
|
|
* value, or a struct of the form {'faultCode': int, 'faultString': string}. This is useful when you need to make
|
|
* lots of small calls without lots of round trips.
|
|
*
|
|
* @param array $calls An array of call requests
|
|
* @return array
|
|
*/
|
|
public function multicall(array $calls)
|
|
{
|
|
return $this->_rpcCall('system', 'multicall', $calls);
|
|
}
|
|
|
|
// Methods added by the Twiddler RPC extension from
|
|
// https://github.com/mnaberez/supervisor_twiddler
|
|
|
|
/**
|
|
* Checks if the Twiddler extension is installed and configured in supervisord.conf
|
|
*
|
|
* @return bool true if the extension is available, else false
|
|
*/
|
|
public function isTwiddlerAvailable()
|
|
{
|
|
$methods = $this->listMethods();
|
|
return in_array('twiddler.getAPIVersion', $methods);
|
|
}
|
|
|
|
/**
|
|
* Return the version of the Twiddler API
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getTwiddlerAPIVersion()
|
|
{
|
|
return $this->_rpcCall('twiddler', 'getAPIVersion');
|
|
}
|
|
|
|
/**
|
|
* Return the group names
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getGroupNames()
|
|
{
|
|
return $this->_rpcCall('twiddler', 'getGroupNames');
|
|
}
|
|
|
|
/**
|
|
* Add a program to a group
|
|
*
|
|
* @param string $group The name of the group
|
|
* @param string $program The name of the program
|
|
* @param array $options Options
|
|
* @return boolean
|
|
*/
|
|
public function addProgramToGroup($group, $program, $options = array())
|
|
{
|
|
return $this->_rpcCall('twiddler', 'addProgramToGroup', array($group, $program, $options));
|
|
}
|
|
|
|
/**
|
|
* Remove a process from a group
|
|
*
|
|
* @param string $group The name of the group
|
|
* @param string $processName The name of the process
|
|
* @return boolean
|
|
*/
|
|
public function removeProcessFromGroup($group, $processName)
|
|
{
|
|
return $this->_rpcCall('twiddler', 'removeProcessFromGroup', array($group, $processName));
|
|
}
|
|
|
|
/**
|
|
* Log a message to the supervisor log
|
|
*
|
|
* @param string $msg The message
|
|
* @param string $level The level (CRIT, ERRO, WARN, INFO, DEBG, TRAC or BLAT)
|
|
* @return boolean
|
|
*/
|
|
public function logMessage($msg, $level = "INFO")
|
|
{
|
|
return $this->_rpcCall('twiddler', 'log', array($msg, $level));
|
|
}
|
|
|
|
/**
|
|
* @param string $namespace The namespace of the request
|
|
* @param string $method The method in the namespace
|
|
* @param mixed $args Optional arguments
|
|
* @return mixed
|
|
* @throws \Exception
|
|
*/
|
|
protected function _rpcCall($namespace, $method, $args = array())
|
|
{
|
|
if (!is_array($args)) {
|
|
$args = array($args);
|
|
}
|
|
// Send the request to the supervisor XML-RPC API.
|
|
return $this->_doRequest($namespace, $method, $args);
|
|
}
|
|
|
|
/**
|
|
* Do a request to the supervisor XML-RPC API
|
|
*
|
|
* @param string $namespace The namespace of the request
|
|
* @param string $method The method in the namespace
|
|
* @param mixed $args Optional arguments
|
|
*/
|
|
protected function _doRequest($namespace, $method, $args)
|
|
{
|
|
$result = [];
|
|
$hostname = $this->_hostname;
|
|
if ($this->_current_hostname) {
|
|
$hostname = [$this->_current_hostname];
|
|
}
|
|
foreach ($hostname as $_hostname) {
|
|
$client = new Client($_hostname . ":" . $this->_port . "/RPC2");
|
|
$client->return_type = "phpvals";
|
|
$response = $client->send(new Request("$namespace.$method", $args));
|
|
$result[$_hostname] = $response;
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
public function getHostname()
|
|
{
|
|
return $this->_hostname;
|
|
}
|
|
|
|
public function getHostNum()
|
|
{
|
|
return $this->_host_num;
|
|
}
|
|
|
|
/**
|
|
* Close the socket when the class destructs
|
|
*/
|
|
public function __destruct()
|
|
{
|
|
if (is_resource($this->_socket)) {
|
|
fclose($this->_socket);
|
|
}
|
|
}
|
|
} |