diff --git a/contrib/IXR/IXR_Library.php b/contrib/IXR/IXR_Library.php index 2f2c814c4..3cbe7344f 100644 --- a/contrib/IXR/IXR_Library.php +++ b/contrib/IXR/IXR_Library.php @@ -176,6 +176,7 @@ class IXR_Message var $faultString; var $methodName; var $params; + var $currentTag; // Current variable stacks var $_arraystructs = array(); // The stack used to keep track of the current array/struct diff --git a/plist b/plist index 64d9d4347..91199c5ad 100644 --- a/plist +++ b/plist @@ -1259,6 +1259,7 @@ /usr/local/opnsense/scripts/system/crl_fetch.py /usr/local/opnsense/scripts/system/flush_config_history /usr/local/opnsense/scripts/system/get_locales.php +/usr/local/opnsense/scripts/system/ha_xmlrpc_exec.php /usr/local/opnsense/scripts/system/nameservers.php /usr/local/opnsense/scripts/system/remote_backup.php /usr/local/opnsense/scripts/system/rfc5246_cipher_suites.csv diff --git a/src/etc/inc/XMLRPC_Client.inc b/src/etc/inc/XMLRPC_Client.inc index 36c06fd93..1acf86837 100644 --- a/src/etc/inc/XMLRPC_Client.inc +++ b/src/etc/inc/XMLRPC_Client.inc @@ -28,6 +28,56 @@ require_once("IXR/IXR_Library.php"); +/** + * Simple wrapper around xmlrpc sync using configuration data. + * In the long run we should likely switch to RESTful implementations as xmlrpc is rather old and less common + * nowadays. + * + * @param string $method method to call + * @param array $params parameters to pass + * @param bool $debug debug mode + * @return array + */ +function xmlrpc_execute($method, $params = [], $debug = false) +{ + global $config; + $synchronizeto = null; + $hasync = $config['hasync']; + if (is_ipaddrv6($hasync['synchronizetoip'])) { + $hasync['synchronizetoip'] = "[{$hasync['synchronizetoip']}]"; + } + + if (!empty($hasync['synchronizetoip'])) { + // determine target url + if (substr($hasync['synchronizetoip'],0, 4) == 'http') { + // URL provided + if (substr($hasync['synchronizetoip'], strlen($hasync['synchronizetoip'])-1, 1) == '/') { + $synchronizeto = $hasync['synchronizetoip']."xmlrpc.php"; + } else { + $synchronizeto = $hasync['synchronizetoip']."/xmlrpc.php"; + } + } elseif (!empty($config['system']['webgui']['protocol'])) { + // no url provided, assume the backup is using the same settings as our box. + $port = $config['system']['webgui']['port']; + if (!empty($port)) { + $synchronizeto = $config['system']['webgui']['protocol'] . '://'.$hasync['synchronizetoip'].':'.$port."/xmlrpc.php"; + } else { + $synchronizeto = $config['system']['webgui']['protocol'] . '://'.$hasync['synchronizetoip']."/xmlrpc.php" ; + } + } + + $username = empty($hasync['username']) ? "root" : $hasync['username']; + $client = new SimpleXMLRPC_Client($synchronizeto,240); + $client->debug=$debug; + $client->setCredentials($username, $hasync['password']); + if ($client->query($method, $params)) { + return $client->getResponse(); + } + } + return false; +} + + /** * Simple XMLRPC client based on the components from IXR * mainly used for backward compatibility of ha sync feature @@ -64,6 +114,13 @@ class SimpleXMLRPC_Client */ private $timeout = 60; + + /** + * request url + * @var string + */ + private $url = ''; + /** * (last) response message * @var null diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Core/Api/HasyncStatusController.php b/src/opnsense/mvc/app/controllers/OPNsense/Core/Api/HasyncStatusController.php new file mode 100644 index 000000000..cb181585b --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/Core/Api/HasyncStatusController.php @@ -0,0 +1,103 @@ +configdRun('system ha exec exec_sync'); + $backend->configdRun('system ha exec reload_templates'); + return json_decode($backend->configdpRun('system ha exec', [$action, $service, $service_id]), true); + } + + public function versionAction() + { + return json_decode((new Backend())->configdRun('system ha exec version'), true); + } + + public function servicesAction() + { + $data = json_decode((new Backend())->configdRun('system ha services_cached'), true); + $records = !empty($data['response']) ? $data['response'] : []; + return $this->searchRecordsetBase($records); + } + + public function stopAction($service=null, $service_id=null) + { + if ($this->request->isPost()) { + return $this->remoteServiceAction('stop', $service, $service_id); + } + return ["status" => "failed"]; + } + + public function startAction($service=null, $service_id=null) + { + if ($this->request->isPost()) { + return $this->remoteServiceAction('start', $service, $service_id); + } + return ["status" => "failed"]; + } + + public function restartAction($service=null, $service_id=null) + { + if ($this->request->isPost()) { + return $this->remoteServiceAction('restart', $service, $service_id); + } + return ["status" => "failed"]; + } + + public function restartAllAction($service=null, $service_id=null) + { + if (true || $this->request->isPost()) { + $backend = new Backend(); + + $services = json_decode((new Backend())->configdRun('system ha exec services'), true); + if (!empty($services['response'])) { + $backend->configdRun('system ha exec exec_sync'); + $backend->configdRun('system ha exec reload_templates'); + foreach ($services['response'] as $service) { + $backend->configdpRun('system ha exec', ['restart', $service['name'], $service['id'] ?? '']); + } + return ["status" => "ok", "count" => count($services['response'])]; + } + + return $this->remoteServiceAction('restart', $service, $service_id); + } + return ["status" => "failed"]; + } +} diff --git a/src/opnsense/scripts/system/ha_xmlrpc_exec.php b/src/opnsense/scripts/system/ha_xmlrpc_exec.php new file mode 100755 index 000000000..a736f020d --- /dev/null +++ b/src/opnsense/scripts/system/ha_xmlrpc_exec.php @@ -0,0 +1,70 @@ +#!/usr/local/bin/php + $service, "id" => $service_id]); + echo json_encode(["response" => $result, 'status' => 'ok']); + break; + case 'start': + $result=xmlrpc_execute('opnsense.start_service', ["service" => $service, "id" => $service_id]); + echo json_encode(["response" => $result, 'status' => 'ok']); + break; + case 'restart': + $result=xmlrpc_execute('opnsense.restart_service', ["service" => $service, "id" => $service_id]); + echo json_encode(["response" => $result, 'status' => 'ok']); + break; + case 'reload_templates': + xmlrpc_execute('opnsense.configd_reload_all_templates'); + echo json_encode(["status" => "done"]); + break; + case 'exec_sync': + configd_run('filter sync'); + echo json_encode(["status" => "done"]); + break; + case 'version': + echo json_encode(["response" => xmlrpc_execute('opnsense.firmware_version')]); + break; + case 'services': + echo json_encode(["response" => xmlrpc_execute('opnsense.list_services')]); + break; + default: + echo json_encode(['status' => 'error', 'message' => 'usage ha_xmlrpc_exec.php action [service_id]']); +} + + + diff --git a/src/opnsense/service/conf/actions.d/actions_system.conf b/src/opnsense/service/conf/actions.d/actions_system.conf index 107d92b00..b5ca401c8 100644 --- a/src/opnsense/service/conf/actions.d/actions_system.conf +++ b/src/opnsense/service/conf/actions.d/actions_system.conf @@ -117,6 +117,19 @@ type:script_output message: Return ha sync options cache_ttl:60 +[ha.exec] +command:/usr/local/opnsense/scripts/system/ha_xmlrpc_exec.php +parameters:%s %s %s +type:script_output +message: execute ha action %s (%s %s) on ha node + +[ha.services_cached] +command:/usr/local/opnsense/scripts/system/ha_xmlrpc_exec.php services +parameters: +type:script_output +message: query remote service list +cache_ttl:5 + [list.nameservers] command:/usr/local/opnsense/scripts/system/nameservers.php parameters:%s