diff --git a/plist b/plist
index d4aa563f0..e803092c7 100644
--- a/plist
+++ b/plist
@@ -35,6 +35,7 @@
/usr/local/etc/inc/plugins.inc.d/ipfw.inc
/usr/local/etc/inc/plugins.inc.d/ipsec.inc
/usr/local/etc/inc/plugins.inc.d/ipsec/auth-user.php
+/usr/local/etc/inc/plugins.inc.d/monit.inc
/usr/local/etc/inc/plugins.inc.d/netflow.inc
/usr/local/etc/inc/plugins.inc.d/ntpd.inc
/usr/local/etc/inc/plugins.inc.d/ntpd/ntpdate_sync_once.sh
@@ -374,6 +375,15 @@
/usr/local/opnsense/mvc/app/controllers/OPNsense/IDS/forms/dialogRuleset.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/IDS/forms/dialogUserDefined.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/IDS/forms/generalSettings.xml
+/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/Api/ServiceController.php
+/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/Api/SettingsController.php
+/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/Api/StatusController.php
+/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/IndexController.php
+/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/StatusController.php
+/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/forms/alerts.xml
+/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/forms/general.xml
+/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/forms/services.xml
+/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/forms/tests.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/Proxy/Api/ServiceController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/Proxy/Api/SettingsController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/Proxy/IndexController.php
@@ -481,6 +491,10 @@
/usr/local/opnsense/mvc/app/models/OPNsense/IDS/IDS.xml
/usr/local/opnsense/mvc/app/models/OPNsense/IDS/Menu/Menu.xml
/usr/local/opnsense/mvc/app/models/OPNsense/IDS/Migrations/M1_0_0.php
+/usr/local/opnsense/mvc/app/models/OPNsense/Monit/ACL/ACL.xml
+/usr/local/opnsense/mvc/app/models/OPNsense/Monit/Menu/Menu.xml
+/usr/local/opnsense/mvc/app/models/OPNsense/Monit/Monit.php
+/usr/local/opnsense/mvc/app/models/OPNsense/Monit/Monit.xml
/usr/local/opnsense/mvc/app/models/OPNsense/Ntpd/ACL/ACL.xml
/usr/local/opnsense/mvc/app/models/OPNsense/Ntpd/Menu/Menu.xml
/usr/local/opnsense/mvc/app/models/OPNsense/Proxy/ACL/ACL.xml
@@ -512,6 +526,8 @@
/usr/local/opnsense/mvc/app/views/OPNsense/Diagnostics/systemactivity.volt
/usr/local/opnsense/mvc/app/views/OPNsense/Diagnostics/systemhealth.volt
/usr/local/opnsense/mvc/app/views/OPNsense/IDS/index.volt
+/usr/local/opnsense/mvc/app/views/OPNsense/Monit/index.volt
+/usr/local/opnsense/mvc/app/views/OPNsense/Monit/status.volt
/usr/local/opnsense/mvc/app/views/OPNsense/Proxy/index.volt
/usr/local/opnsense/mvc/app/views/OPNsense/Routes/index.volt
/usr/local/opnsense/mvc/app/views/OPNsense/TrafficShaper/index.volt
@@ -572,6 +588,8 @@
/usr/local/opnsense/scripts/OPNsense/CaptivePortal/set_session_restrictions.py
/usr/local/opnsense/scripts/OPNsense/CaptivePortal/sql/init.sql
/usr/local/opnsense/scripts/OPNsense/CaptivePortal/strip_template.py
+/usr/local/opnsense/scripts/OPNsense/Monit/post-install.php
+/usr/local/opnsense/scripts/OPNsense/Monit/setup.sh
/usr/local/opnsense/scripts/dhcp/get_leases.py
/usr/local/opnsense/scripts/dhcp/prefixes.php
/usr/local/opnsense/scripts/dns/unbound_dhcpd.py
@@ -681,6 +699,7 @@
/usr/local/opnsense/service/conf/actions.d/actions_interface.conf
/usr/local/opnsense/service/conf/actions.d/actions_ipfw.conf
/usr/local/opnsense/service/conf/actions.d/actions_ipsec.conf
+/usr/local/opnsense/service/conf/actions.d/actions_monit.conf
/usr/local/opnsense/service/conf/actions.d/actions_netflow.conf
/usr/local/opnsense/service/conf/actions.d/actions_openssh.conf
/usr/local/opnsense/service/conf/actions.d/actions_openvpn.conf
@@ -733,6 +752,9 @@
/usr/local/opnsense/service/templates/OPNsense/IPFW/ipfw.fw.conf
/usr/local/opnsense/service/templates/OPNsense/IPFW/rc.conf.d
/usr/local/opnsense/service/templates/OPNsense/Macros/interface.macro
+/usr/local/opnsense/service/templates/OPNsense/Monit/+TARGETS
+/usr/local/opnsense/service/templates/OPNsense/Monit/monitrc
+/usr/local/opnsense/service/templates/OPNsense/Monit/rc.conf.d
/usr/local/opnsense/service/templates/OPNsense/Netflow/+TARGETS
/usr/local/opnsense/service/templates/OPNsense/Netflow/flowd.conf
/usr/local/opnsense/service/templates/OPNsense/Netflow/flowd.rc.conf.d
diff --git a/src/etc/inc/plugins.inc.d/monit.inc b/src/etc/inc/plugins.inc.d/monit.inc
new file mode 100644
index 000000000..b8f1a5382
--- /dev/null
+++ b/src/etc/inc/plugins.inc.d/monit.inc
@@ -0,0 +1,66 @@
+ gettext('Monit System Monitoring'),
+ 'configd' => array(
+ 'restart' => array('monit restart'),
+ 'start' => array('monit start'),
+ 'stop' => array('monit stop'),
+ ),
+ 'name' => 'monit',
+ );
+ }
+ return $services;
+}
+
+/**
+ * sync configuration via xmlrpc
+ * @return array
+ */
+function monit_xmlrpc_sync()
+{
+ $result = array();
+
+ $result[] = array(
+ 'description' => gettext('Monit System Monitoring'),
+ 'section' => 'OPNsense.monit',
+ 'id' => 'monit',
+ );
+
+ return $result;
+}
diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/ServiceController.php b/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/ServiceController.php
new file mode 100644
index 000000000..0b1a73522
--- /dev/null
+++ b/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/ServiceController.php
@@ -0,0 +1,79 @@
+request->isPost()) {
+ $this->sessionClose();
+
+ $backend = new Backend();
+
+ $result['function'] = "configtest";
+ $result['template'] = trim($backend->configdRun('template reload OPNsense/Monit'));
+ if ($result['template'] != 'OK') {
+ $result['result'] = "Template error: " . $result['template'];
+ return $result;
+ }
+ $result['result'] = trim($backend->configdRun('monit configtest'));
+ return $result;
+ } else {
+ return array('status' => 'failed');
+ }
+ }
+
+ /**
+ * avoid restarting Relayd on reconfigure
+ */
+ protected function reconfigureForceRestart()
+ {
+ return 0;
+ }
+}
diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/SettingsController.php b/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/SettingsController.php
new file mode 100644
index 000000000..621aaaf45
--- /dev/null
+++ b/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/SettingsController.php
@@ -0,0 +1,341 @@
+ "failed");
+ if ($this->request->isGet() && $nodeType != null) {
+ $this->validateNodeType($nodeType);
+ $mdlMonit = new Monit();
+ if ($nodeType == 'general') {
+ $node = $mdlMonit->getNodeByReference($nodeType);
+ } else {
+ if ($uuid != null) {
+ $node = $mdlMonit->getNodeByReference($nodeType . '.' . $uuid);
+ } else {
+ $node = $mdlMonit->$nodeType->Add();
+ }
+ }
+ if ($node != null) {
+ $result[$nodeType] = $node->getNodes();
+ $result["result"] = "ok";
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * set monit properties
+ * @param $nodeType
+ * @param $uuid
+ * @return status array
+ */
+ public function setAction($nodeType = null, $uuid = null)
+ {
+ $result = array("result" => "failed", "validations" => array());
+ if ($this->request->isPost() && $this->request->hasPost("monit") && $nodeType != null) {
+ $this->validateNodeType($nodeType);
+ $mdlMonit = new Monit();
+ if ($nodeType == 'general') {
+ $node = $mdlMonit->getNodeByReference($nodeType);
+ } else {
+ if ($uuid != null) {
+ $node = $mdlMonit->getNodeByReference($nodeType . '.' . $uuid);
+ } else {
+ $node = $mdlMonit->$nodeType->Add();
+ }
+ }
+ if ($node != null) {
+ $monitInfo = $this->request->getPost("monit");
+
+ // perform plugin specific validations
+ if ($nodeType == 'service') {
+ switch ($monitInfo[$nodeType]['type']) {
+ case 'process':
+ if (empty($monitInfo[$nodeType]['pidfile']) && empty($monitInfo[$nodeType]['match'])) {
+ $result["validations"]['monit.service.pidfile'] = "Please set at least one of Pidfile or Match.";
+ $result["validations"]['monit.service.match'] = $result["validations"]['monit.service.pidfile'];
+ }
+ break;
+ case 'host':
+ if (empty($monitInfo[$nodeType]['address'])) {
+ $result["validations"]['monit.service.address'] = "Address is mandatory for 'Remote Host' checks.";
+ }
+ break;
+ case 'network':
+ if (empty($monitInfo[$nodeType]['address']) && empty($monitInfo[$nodeType]['interface'])) {
+ $result["validations"]['monit.service.address'] = "Please set at least one of Address or Interface.";
+ $result["validations"]['monit.service.interface'] = $result["validations"]['monit.service.address'];
+ }
+ break;
+ case 'system':
+ break;
+ default:
+ if (empty($monitInfo[$nodeType]['path'])) {
+ $result["validations"]['monit.service.path'] = "Path is mandatory.";
+ }
+ }
+ }
+
+ $node->setNodes($monitInfo[$nodeType]);
+ $valMsgs = $mdlMonit->performValidation();
+ foreach ($valMsgs as $field => $msg) {
+ $fieldnm = str_replace($node->__reference, "monit." . $nodeType, $msg->getField());
+ $result["validations"][$fieldnm] = $msg->getMessage();
+ }
+ if (empty($result["validations"])) {
+ unset($result["validations"]);
+ $result['result'] = 'ok';
+ $mdlMonit->serializeToConfig();
+ Config::getInstance()->save();
+ if ($nodeType == 'general' && $monitInfo['general']['enabled'] == '0') {
+ $svcMonit = new ServiceController();
+ $result = $svcMonit->stopAction();
+ }
+ }
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * delete monit settings
+ * @param $nodeType
+ * @param $uuid
+ * @return status array
+ */
+ public function delAction($nodeType = null, $uuid = null)
+ {
+ $result = array("result" => "failed");
+ if ($nodeType != null) {
+ $this->validateNodeType($nodeType);
+ if ($uuid != null) {
+ $mdlMonit = new Monit();
+ $node = $mdlMonit->getNodeByReference($nodeType . '.' . $uuid);
+ if ($node != null) {
+ if ($mdlMonit->$nodeType->del($uuid) == true) {
+ // delete relations
+ if ($nodeType == 'test') {
+ $nodeName = $mdlMonit->getNodeByReference($nodeType . '.' . $uuid . '.name');
+ if ($nodeName != null) {
+ $nodeName = $nodeName->__toString();
+ $this->deleteRelations('service', 'tests', $uuid, 'test', $nodeName, $mdlMonit);
+ }
+ }
+ $mdlMonit->serializeToConfig();
+ Config::getInstance()->save();
+ $result["result"] = "ok";
+ }
+ }
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * toggle monit items (enable/disable)
+ * @param $nodeType
+ * @param $uuid
+ * @return result array
+ */
+ public function toggleAction($nodeType = null, $uuid = null)
+ {
+ $result = array("result" => "failed");
+ if ($this->request->isPost() && $nodeType != null) {
+ $mdlMonit = new Monit();
+ if ($uuid != null) {
+ $node = $mdlMonit->getNodeByReference($nodeType . '.' . $uuid);
+ if ($node != null) {
+ if ($node->enabled->__toString() == "1") {
+ $node->enabled = "0";
+ } else {
+ $node->enabled = "1";
+ }
+ $mdlMonit->serializeToConfig();
+ Config::getInstance()->save();
+ $svcMonit = new ServiceController();
+ $result= $svcMonit->reloadAction();
+ } else {
+ $result['result'] = "not found";
+ }
+ } else {
+ $result['result'] = "uuid not given";
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * search monit settings
+ * @param $nodeType
+ * @return result array
+ */
+ public function searchAction($nodeType = null)
+ {
+ $this->sessionClose();
+ if ($this->request->isPost() && $nodeType != null) {
+ $this->validateNodeType($nodeType);
+ $mdlMonit = new Monit();
+ $grid = new UIModelGrid($mdlMonit->$nodeType);
+ $fields = array();
+ switch ($nodeType) {
+ case 'alert':
+ $fields = array("enabled", "recipient", "noton", "events", "description");
+ break;
+ case 'service':
+ $fields = array("enabled", "name", "type", "description");
+ break;
+ case 'test':
+ $fields = array("name", "condition", "action");
+ break;
+ }
+ return $grid->fetchBindRequest($this->request, $fields);
+ }
+ }
+
+ /**
+ * import system notification settings
+ * @return result array
+ */
+ public function notificationAction()
+ {
+ $result = array("result" => "failed");
+ if ($this->request->isPost()) {
+ $this->sessionClose();
+
+ $cfg = Config::getInstance();
+ $cfgObj = $cfg->object();
+ $mdlMonit = new Monit();
+ $node = $mdlMonit->getNodeByReference('general');
+ $generalSettings = array();
+
+ // inherit SMTP settings from System->Settings->Notifications
+ if (!empty($cfgObj->notifications->smtp->ipaddress)) {
+ $generalSettings['mailserver'] = $cfgObj->notifications->smtp->ipaddress;
+ }
+ if (!empty($cfgObj->notifications->smtp->port)) {
+ $generalSettings['port'] = $cfgObj->notifications->smtp->port;
+ }
+ $generalSettings['username'] = $cfgObj->notifications->smtp->username;
+ $generalSettings['password'] = $cfgObj->notifications->smtp->password;
+ if ((!empty($cfgObj->notifications->smtp->tls) && $cfgObj->notifications->smtp->tls == 1) ||
+ (!empty($cfgObj->notifications->smtp->ssl) && $cfgObj->notifications->smtp->ssl == 1)) {
+ $generalSettings['ssl'] = 1;
+ } else {
+ $generalSettings['ssl'] = 0;
+ }
+
+ // apply them
+ $node->setNodes($generalSettings);
+ $valMsgs = $mdlMonit->performValidation();
+ foreach ($valMsgs as $field => $msg) {
+ $fieldnm = str_replace($node->__reference, "monit.general.", $msg->getField());
+ $result["validations"][$fieldnm] = $msg->getMessage();
+ }
+ if (empty($result["validations"])) {
+ unset($result["validations"]);
+ $result['result'] = 'ok';
+ $mdlMonit->serializeToConfig();
+ Config::getInstance()->save();
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * validate nodeType
+ * @param $nodeType
+ * @throws \Exception
+ */
+ private function validateNodeType($nodeType = null)
+ {
+ if (array_search($nodeType, $this->nodeTypes) === false) {
+ throw new \Exception('unknown nodeType: ' . $nodeType);
+ }
+ }
+
+ /**
+ * delete relations
+ * @param $nodeType
+ * @param $uuid
+ * @param $relNodeType
+ * @param &$mdlMonit
+ * @throws \Exception
+ */
+ private function deleteRelations($nodeType = null, $nodeField = null, $relUuid = null, $relNodeType = null, $relNodeName = null, &$mdlMonit = null)
+ {
+ $nodes = $mdlMonit->$nodeType->getNodes();
+ // get nodes with relations
+ foreach ($nodes as $nodeUuid => $node) {
+ // get relation uuids
+ foreach ($node[$nodeField] as $fieldUuid => $field) {
+ // remove uuid from field
+ if ($fieldUuid == $relUuid) {
+ $refField = $nodeType . '.' . $nodeUuid . '.' . $nodeField;
+ $relNode = $mdlMonit->getNodeByReference($refField);
+ $nodeRels = str_replace($relUuid, '', $relNode->__toString());
+ $nodeRels = str_replace(',,', ',', $nodeRels);
+ $nodeRels = rtrim($nodeRels, ',');
+ $nodeRels = ltrim($nodeRels, ',');
+ $mdlMonit->setNodeByReference($refField, $nodeRels);
+ if ($relNode->isEmptyAndRequired()) {
+ $nodeName = $mdlMonit->getNodeByReference($nodeType . '.' . $nodeUuid . '.name')->__toString();
+ throw new \Exception("Cannot delete $relNodeType '$relNodeName' from $nodeType '$nodeName'");
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/StatusController.php b/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/StatusController.php
new file mode 100644
index 000000000..65646590a
--- /dev/null
+++ b/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/StatusController.php
@@ -0,0 +1,144 @@
+ "failed");
+
+ $socketPath = "/var/run/monit.sock";
+
+ // map the requested html format from the status page to the Monit text format
+ $format = $format == 'html' ? 'text' : $format;
+
+ // check monit httpd socket defined in monitrc by 'set httpd ...'
+ if (file_exists($socketPath) && filetype($socketPath) == "socket") {
+ // set curl options
+ $ch = curl_init("http://127.0.0.1/_status?format=" . $format);
+ curl_setopt($ch, CURLOPT_UNIX_SOCKET_PATH, $socketPath);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+
+ // get credentials if configured
+ $mdlMonit = new Monit();
+ if ($mdlMonit->general->httpdUsername->__toString() != null && trim($mdlMonit->general->httpdUsername->__toString()) !== "" &&
+ $mdlMonit->general->httpdPassword->__toString() != null && trim($mdlMonit->general->httpdPassword->__toString()) !== "") {
+ curl_setopt($ch, CURLOPT_USERPWD, $mdlMonit->general->httpdUsername->__toString() . ":" . $mdlMonit->general->httpdPassword->__toString());
+ }
+
+ // send request
+ if (!$response = curl_exec($ch)) {
+ $result['status'] = curl_error($ch);
+ return $result;
+ }
+ $HTTPCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+
+ if ($HTTPCode != 200) {
+ $result['status'] = 'Monit returns with code ' . $HTTPCode;
+ } else {
+ $result['result'] = "ok";
+
+ // format the response
+ if ($format == 'xml') {
+ $result['status'] = simplexml_load_string($response);
+ } elseif ($format === 'text') {
+ $result['status'] = '
' . $this->bashColorToCSS($response) . ' ';
+ }
+ }
+ } else {
+ $msg = "
+Either the file " . $socketPath . " does not exists or it is not a unix socket.
+Please check if the Monit service is running.
+
+If you have started Monit recently, wait for StartDelay seconds and refresh this page.";
+ if ($format == 'xml') {
+ $result['status'] = $msg;
+ } elseif ($format === 'text') {
+ $result['status'] = '' . $msg . ' ';
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * convert bash color escape codes to CSS
+ * @param $string
+ * @return string
+ */
+ private function bashColorToCSS($string)
+ {
+ $colors = [
+ '/\x1b\[0;30m(.*?)\x1b\[0m/s' => '$1 ',
+
+ '/\x1b\[0;30m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[0;31m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[0;32m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[0;33m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[0;34m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[0;35m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[0;36m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[0;37m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[0;39m(.*?)\x1b\[0m/s' => '$1 ',
+
+ '/\x1b\[1;30m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[1;31m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[1;32m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[1;33m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[1;34m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[1;35m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[1;36m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[1;37m(.*?)\x1b\[0m/s' => '$1 ',
+
+ '/\x1b\[0;90m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[0;91m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[0;92m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[0;93m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[0;94m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[0;95m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[0;96m(.*?)\x1b\[0m/s' => '$1 ',
+ '/\x1b\[0;97m(.*?)\x1b\[0m/s' => '$1 '
+ ];
+ return preg_replace(array_keys($colors), $colors, $string);
+ }
+}
diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Monit/IndexController.php b/src/opnsense/mvc/app/controllers/OPNsense/Monit/IndexController.php
new file mode 100644
index 000000000..86c38d3d3
--- /dev/null
+++ b/src/opnsense/mvc/app/controllers/OPNsense/Monit/IndexController.php
@@ -0,0 +1,51 @@
+view->formGeneralSettings = $this->getForm("general");
+ $this->view->formDialogEditAlert = $this->getForm("alerts");
+ $this->view->formDialogEditService = $this->getForm("services");
+ $this->view->formDialogEditTest = $this->getForm("tests");
+ $this->view->pick('OPNsense/Monit/index');
+ }
+}
diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Monit/StatusController.php b/src/opnsense/mvc/app/controllers/OPNsense/Monit/StatusController.php
new file mode 100644
index 000000000..963836f19
--- /dev/null
+++ b/src/opnsense/mvc/app/controllers/OPNsense/Monit/StatusController.php
@@ -0,0 +1,46 @@
+view->pick('OPNsense/Monit/status');
+ }
+}
diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/alerts.xml b/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/alerts.xml
new file mode 100644
index 000000000..643de6d59
--- /dev/null
+++ b/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/alerts.xml
@@ -0,0 +1,47 @@
+
diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/general.xml b/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/general.xml
new file mode 100644
index 000000000..1f5e476f4
--- /dev/null
+++ b/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/general.xml
@@ -0,0 +1,124 @@
+
diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/services.xml b/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/services.xml
new file mode 100644
index 000000000..15d0f2c8c
--- /dev/null
+++ b/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/services.xml
@@ -0,0 +1,74 @@
+
diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/tests.xml b/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/tests.xml
new file mode 100644
index 000000000..5d96a0a21
--- /dev/null
+++ b/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/tests.xml
@@ -0,0 +1,26 @@
+
diff --git a/src/opnsense/mvc/app/models/OPNsense/Monit/ACL/ACL.xml b/src/opnsense/mvc/app/models/OPNsense/Monit/ACL/ACL.xml
new file mode 100644
index 000000000..72937387e
--- /dev/null
+++ b/src/opnsense/mvc/app/models/OPNsense/Monit/ACL/ACL.xml
@@ -0,0 +1,10 @@
+
+
+ WebCfg - Services: Monit System Monitoring page
+ Allow access to the 'Services: Monit System Monitoring' page.
+
+ ui/monit/*
+ api/monit/*
+
+
+
diff --git a/src/opnsense/mvc/app/models/OPNsense/Monit/Menu/Menu.xml b/src/opnsense/mvc/app/models/OPNsense/Monit/Menu/Menu.xml
new file mode 100644
index 000000000..841575d23
--- /dev/null
+++ b/src/opnsense/mvc/app/models/OPNsense/Monit/Menu/Menu.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.php b/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.php
new file mode 100644
index 000000000..a386f7c00
--- /dev/null
+++ b/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.php
@@ -0,0 +1,41 @@
+
+ //OPNsense/monit
+ 1.0.3
+ Monit settings
+
+
+
+ 0
+ Y
+
+
+ 120
+ Y
+ 0
+ 86400
+ Polling Interval needs to be an integer value between 0 and 86400
+
+
+ 120
+ Y
+ 0
+ 86400
+ Start Delay needs to be an integer value between 0 and 86400
+
+
+ 127.0.0.1
+ Y
+ Y
+ /^([0-9a-zA-Z\.,_\-:]){0,1024}$/u
+ lower
+ Please specify a valid servername or IP address.
+
+
+ Y
+ 25
+ 0
+ 65535
+ Port needs to be an integer value between 0 and 65535
+
+
+ N
+
+
+ N
+
+
+ 0
+ Y
+
+
+ N
+ syslog facility log_daemon
+ /^(\/[^\/ ]*)+\/?|syslog.*$/
+ Should be a valid absolute path to the log file or the keyword "syslog".
+
+
+ N
+ /^(\/[^\/ ]*)+\/?$/
+ Should be a valid absolute path to the state file.
+
+
+ N
+ /^(\/[^\/ ]*)+\/?$/
+ Should be a valid absolute path to the eventqueue directory.
+
+
+ N
+ Eventqueue Slots must be a numeric value.
+
+
+ 0
+ Y
+
+
+
+
+ 2812
+ Y
+ 1
+ 65535
+ Local Monit Port needs to be an integer value between 0 and 65535
+
+
+ N
+ Y
+
+
+ N
+
+
+ 5
+ Y
+ 0
+ 86400
+ M/Monit Timeout needs to be an integer value between 0 and 86400
+
+
+ 1
+ Y
+
+
+
+
+ 0
+ Y
+
+
+ root@localhost.local
+ Y
+ Please enter a valid email address.
+
+
+ Y
+ 0
+
+
+ N
+
+ Action done
+ Checksum failed
+ Download bytes exceeded
+ Upload bytes exceeded
+ Connection failed
+ Content failed
+ Data access error
+ Execution failed
+ Filesystem flags failed
+ GID failed
+ Ping failed
+ Monit instance changed
+ Invalid type
+ Does not exist
+ Download packets exceeded
+ Upload packets exceeded
+ Permission failed
+ PID failed
+ PPID failed
+ Resource limit matched
+ Saturation exceeded
+ Size failed
+ Speed failed
+ Status failed
+ Timeout
+ Timestamp failed
+ UID failed
+ Uptime failed
+
+
+
+ N
+ /^.{1,255}$/u
+ Message format should be a string between 1 and 255 characters.
+
+
+ 10
+ N
+ 0
+ 86400
+ Reminder needs to be an integer value between 0 and 86400
+
+
+ N
+ /^.{1,255}$/u
+ Enter a description.
+
+
+
+
+ 0
+ Y
+
+
+ Y
+ /^([0-9a-zA-Z\._\-\$]){1,255}$/u
+ Should be a string between 1 and 255 characters. Allowed characters are letters and numbers as well as underscore, minus, dot and the dollar sign.
+
+
+ Y
+
+ Process
+ File
+ Fifo
+ Filesystem
+ Directory
+ Remote Host
+ System
+ Custom
+ Network
+
+
+
+ N
+ /^(\/[^\/ ]*)+\/?$/
+ Should be a valid absolute path to the PID file of the process.
+
+
+ N
+
+
+ N
+ /^(\/[^\/ ]*)+\/?.*$/
+ Should be a valid absolute file or folder path.
+
+
+ N
+ /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-4]|2[0-5][0-9]|[01]?[0-9][0-9]?)$/
+ Address must be a valid IPv4 address
+
+
+ N
+ N
+
+ /^(?!0).*$/
+ /^((?!dhcp).)*$/
+
+
+
+ N
+ /^(\/[^\/ ]*)+\/? .*$/
+ Should be a valid absolute path to the executable with its arguments.
+
+
+ N
+ /^(\/[^\/ ]*)+\/? .*$/
+ Should be a valid absolute path to the executable with its arguments.
+
+
+
+
+ OPNsense.monit.monit
+ test
+ name
+
+
+ Related item not found
+ Y
+ N
+
+
+
+
+ Y
+ /^([0-9a-zA-Z\._\- ]){1,255}$/u
+ Should be a string between 1 and 255 characters. Allowed characters are letters and numbers as well as underscore, minus, dot and space.
+
+
+ Y
+ /^.{1,255}$/u
+ Should be a string between 1 and 255 characters.
+
+
+ Y
+
+ Alert
+ Restart
+ Start
+ Stop
+ Execute
+ Unmonitor
+
+
+
+ N
+ /^(\/[^\/ ]*)+\/?.*$/
+ Should be a valid absolute file path.
+
+
+
+
diff --git a/src/opnsense/mvc/app/views/OPNsense/Monit/index.volt b/src/opnsense/mvc/app/views/OPNsense/Monit/index.volt
new file mode 100644
index 000000000..5649844b3
--- /dev/null
+++ b/src/opnsense/mvc/app/views/OPNsense/Monit/index.volt
@@ -0,0 +1,372 @@
+{#
+
+Copyright © 2017-2018 by EURO-LOG AG
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+#}
+
+
+
+
+
+
+
+
+
+
+ {{ partial("layout_partials/base_form",['fields':formGeneralSettings,'id':'frm_GeneralSettings','apply_btn_id':'btn_ApplyGeneralSettings'])}}
+
+
+
+
+
+ {{ lang._('Enabled') }}
+ {{ lang._('Recipient') }}
+
+ {{ lang._('Events') }}
+ {{ lang._('Description') }}
+ {{ lang._('ID') }}
+ {{ lang._('Edit') }} | {{ lang._('Delete') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ lang._('Enabled') }}
+ {{ lang._('Name') }}
+ {{ lang._('ID') }}
+ {{ lang._('Edit') }} | {{ lang._('Delete') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ lang._('Name') }}
+ {{ lang._('Condition') }}
+ {{ lang._('Action') }}
+ {{ lang._('ID') }}
+ {{ lang._('Edit') }} | {{ lang._('Delete') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ lang._('Test Configuration') }}
+ {{ lang._('Reload Configuration') }}
+
+
+
+
+{# include dialogs #}
+{{ partial("layout_partials/base_dialog",['fields':formDialogEditAlert,'id':'DialogEditAlert','label':'Edit Alert NOTE: For a detailed description see monit(1) section "ALERT MESSAGES". '])}}
+{{ partial("layout_partials/base_dialog",['fields':formDialogEditService,'id':'DialogEditService','label':'Edit Service'])}}
+{{ partial("layout_partials/base_dialog",['fields':formDialogEditTest,'id':'DialogEditTest','label':'Edit Test NOTE: For a detailed description see monit(1) section "SERVICE TESTS". '])}}
diff --git a/src/opnsense/mvc/app/views/OPNsense/Monit/status.volt b/src/opnsense/mvc/app/views/OPNsense/Monit/status.volt
new file mode 100644
index 000000000..e9c279c01
--- /dev/null
+++ b/src/opnsense/mvc/app/views/OPNsense/Monit/status.volt
@@ -0,0 +1,45 @@
+{#
+
+Copyright © 2017-2018 by EURO-LOG AG
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+#}
+
+
+
+Loading...
+
diff --git a/src/opnsense/scripts/OPNsense/Monit/post-install.php b/src/opnsense/scripts/OPNsense/Monit/post-install.php
new file mode 100755
index 000000000..b46f04aba
--- /dev/null
+++ b/src/opnsense/scripts/OPNsense/Monit/post-install.php
@@ -0,0 +1,161 @@
+#!/usr/local/bin/php
+object();
+$shellObj = new OPNsense\Core\Shell;
+$generalNode = $mdlMonit->getNodeByReference('general');
+
+if (empty($cfgObj->OPNsense->monit->general->httpdUsername) && empty($cfgObj->OPNsense->monit->general->httpdPassword)) {
+ print "Generate Monit httpd username and password\n";
+ srand();
+ $generalNode->setNodes(array(
+ "httpdUsername" => "root",
+ "httpdPassword" => substr(str_shuffle(str_repeat('0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz', 32)), rand(0, 16), rand(17, 32))
+ ));
+ $mdlMonit->serializeToConfig(false, true);
+ $cfg->save();
+}
+
+$nodes = $mdlMonit->getNodes();
+// test if Monit is already configured
+if (count($nodes['service']) != 0 || count($nodes['test']) != 0) {
+ exit;
+}
+
+// get number of cpus and calculate load average limits
+$nCPU = array();
+$shellObj->exec('/sbin/sysctl -n kern.smp.cpus', false, $nCPU);
+$LoadAvg1 = $nCPU[0] * 2;
+$LoadAvg5 = $nCPU[0] + ($nCPU[0] / 2);
+$LoadAvg15 = $nCPU[0];
+
+// inherit SMTP settings from System->Settings->Notifications
+$generalSettings = array();
+if (!empty($cfgObj->notifications->smtp->ipaddress)) {
+ $generalSettings['mailserver'] = $cfgObj->notifications->smtp->ipaddress;
+}
+if (!empty($cfgObj->notifications->smtp->port)) {
+ $generalSettings['port'] = $cfgObj->notifications->smtp->port;
+}
+if (!empty($cfgObj->notifications->smtp->username)) {
+ $generalSettings['username'] = $cfgObj->notifications->smtp->username;
+}
+if (!empty($cfgObj->notifications->smtp->password)) {
+ $generalSettings['password'] = $cfgObj->notifications->smtp->password;
+}
+if ((!empty($cfgObj->notifications->smtp->tls) && $cfgObj->notifications->smtp->tls == 1) ||
+ (!empty($cfgObj->notifications->smtp->ssl) && $cfgObj->notifications->smtp->ssl == 1)) {
+ $generalSettings['ssl'] = 1;
+}
+
+$alertSettings = array();
+if (!empty($cfgObj->notifications->smtp->notifyemailaddress)) {
+ $alertSettings['recipient'] = $cfgObj->notifications->smtp->notifyemailaddress;
+}
+if (!empty($cfgObj->notifications->smtp->fromaddress)) {
+ $alertSettings['format'] = 'from: ' . $cfgObj->notifications->smtp->fromaddress;
+}
+
+// define some tests
+$defaultTests = array(
+ array("name" => "Ping", "condition" => "failed ping", "action" => "alert"),
+ array("name" => "NetworkLink", "condition" => "failed link", "action" => "alert"),
+ array("name" => "NetworkSaturation", "condition" => "saturation is greater than 75%", "action" => "alert"),
+ array("name" => "MemoryUsage", "condition" => "memory usage is greater than 75%", "action" => "alert"),
+ array("name" => "CPUUsage", "condition" => "cpu usage is greater than 75%", "action" => "alert"),
+ array("name" => "LoadAvg1", "condition" => "loadavg (1min) is greater than $LoadAvg1", "action" => "alert"),
+ array("name" => "LoadAvg5", "condition" => "loadavg (5min) is greater than $LoadAvg5", "action" => "alert"),
+ array("name" => "LoadAvg15", "condition" => "loadavg (15min) is greater than $LoadAvg15", "action" => "alert"),
+ array("name" => "SpaceUsage", "condition" => "space usage is greater than 75%", "action" => "alert")
+);
+
+// define system service
+$systemService = array(
+ "enabled" => 1,
+ "name" => '$HOST',
+ "type" => "system",
+ "tests" => ""
+);
+
+// define root filesystem service
+$rootFsService = array(
+ "enabled" => 1,
+ "name" => "RootFs",
+ "type" => "filesystem",
+ "path" => "/",
+ "tests" => ""
+);
+
+foreach ($defaultTests as $defaultTest) {
+ $testNode = $mdlMonit->test->Add();
+ $testNode->setNodes($defaultTest);
+ if ($defaultTest['name'] == "MemoryUsage" ||
+ $defaultTest['name'] == "CPUUsage" ||
+ $defaultTest['name'] == "LoadAvg1" ||
+ $defaultTest['name'] == "LoadAvg5" ) {
+ $systemService['tests'] .= $testNode->getAttributes()['uuid'] . ",";
+ }
+ if ($defaultTest['name'] == "SpaceUsage") {
+ $rootFsService['tests'] .= $testNode->getAttributes()['uuid'] . ",";
+ }
+}
+
+// remove last comma from tests csv
+$systemService['tests'] = substr($systemService['tests'], 0, -1);
+$rootFsService['tests'] = substr($rootFsService['tests'], 0, -1);
+
+// set general properties
+$generalNode->setNodes($generalSettings);
+
+// add an alert with (almost) default settings
+$alertNode = $mdlMonit->alert->Add();
+$alertNode->setNodes($alertSettings);
+
+// add system service
+$serviceNode = $mdlMonit->service->Add();
+$serviceNode->setNodes($systemService);
+
+// add root filesystem service
+$rootFsNode = $mdlMonit->service->Add();
+$rootFsNode->setNodes($rootFsService);
+
+// ignore validations because ModelRelationField does not work
+$mdlMonit->serializeToConfig(false, true);
+$cfg->save();
diff --git a/src/opnsense/scripts/OPNsense/Monit/setup.sh b/src/opnsense/scripts/OPNsense/Monit/setup.sh
new file mode 100755
index 000000000..cf200fd2e
--- /dev/null
+++ b/src/opnsense/scripts/OPNsense/Monit/setup.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+# change permissions of the monit configuration file
+chmod 600 /usr/local/etc/monitrc || exit 1
+exit 0
diff --git a/src/opnsense/service/conf/actions.d/actions_monit.conf b/src/opnsense/service/conf/actions.d/actions_monit.conf
new file mode 100644
index 000000000..025e0185d
--- /dev/null
+++ b/src/opnsense/service/conf/actions.d/actions_monit.conf
@@ -0,0 +1,29 @@
+[start]
+command:/usr/local/opnsense/scripts/OPNsense/Monit/setup.sh; /usr/local/etc/rc.d/monit start
+type:script
+message:starting monit
+
+[stop]
+command:/usr/local/etc/rc.d/monit stop
+type:script
+message:stopping monit
+
+[status]
+command:/usr/local/etc/rc.d/monit status; exit 0
+type:script_output
+message:get monit status
+
+[restart]
+command:/usr/local/opnsense/scripts/OPNsense/Monit/setup.sh; /usr/local/etc/rc.d/monit restart
+type:script
+message:restarting monit
+
+[reload]
+command:/usr/local/opnsense/scripts/OPNsense/Monit/setup.sh; /usr/local/etc/rc.d/monit reload
+type:script
+message:reload monit configuration
+
+[configtest]
+command:/usr/local/opnsense/scripts/OPNsense/Monit/setup.sh; /usr/local/bin/monit -t 2>&1 || exit 0
+type:script_output
+message:testing monit configuration
diff --git a/src/opnsense/service/templates/OPNsense/Monit/+TARGETS b/src/opnsense/service/templates/OPNsense/Monit/+TARGETS
new file mode 100644
index 000000000..d5be9309b
--- /dev/null
+++ b/src/opnsense/service/templates/OPNsense/Monit/+TARGETS
@@ -0,0 +1,2 @@
+monitrc:/usr/local/etc/monitrc
+rc.conf.d:/etc/rc.conf.d/monit
diff --git a/src/opnsense/service/templates/OPNsense/Monit/monitrc b/src/opnsense/service/templates/OPNsense/Monit/monitrc
new file mode 100644
index 000000000..1fa8bc96e
--- /dev/null
+++ b/src/opnsense/service/templates/OPNsense/Monit/monitrc
@@ -0,0 +1,133 @@
+# DO NOT EDIT THIS FILE -- OPNsense auto-generated file
+
+{% from 'OPNsense/Macros/interface.macro' import physical_interface %}
+{% if helpers.exists('OPNsense.monit.general') %}
+{% if helpers.exists('OPNsense.monit.general.httpdEnabled') and OPNsense.monit.general.httpdEnabled|default('0') == '1' %}
+{% set httpdCredentials = OPNsense.monit.general.httpdUsername ~ ':"' ~ OPNsense.monit.general.httpdPassword ~ '"' %}
+{% set httpdport = "port " ~ OPNsense.monit.general.httpdPort %}
+set httpd unixsocket /var/run/monit.sock {{ httpdport }}
+ allow localhost
+ allow {{ httpdCredentials }}
+{% if helpers.exists('OPNsense.monit.general.httpdAllow') %}
+{% for allow in OPNsense.monit.general.httpdAllow.split(",") %}
+{% set cred = allow.split(":") %}
+{% if cred[1] %}
+{% set pass = cred[1].split() %}
+{% set allow = cred[0] ~ ':"' ~ pass[0] ~ '" ' ~ pass[1] %}
+{% endif %}
+ allow {{ allow }}
+{% endfor %}
+{% endif %}
+{% if helpers.exists('OPNsense.monit.general.mmonitUrl') and OPNsense.monit.general.mmonitUrl|default('') != '' %}
+{% set mregister = 'register without credentials' if OPNsense.monit.general.mmonitRegisterCredentials|default('1') == '0' %}
+
+set mmonit {{ OPNsense.monit.general.mmonitUrl }} timeout {{ OPNsense.monit.general.mmonitTimeout }} seconds {{ mregister }}
+{% endif %}
+{% else %}
+set httpd unixsocket /var/run/monit.sock
+ allow localhost
+{% endif %}
+
+set daemon {{ OPNsense.monit.general.interval }} with start delay {{ OPNsense.monit.general.startdelay }}
+
+{% if helpers.exists('OPNsense.monit.general.logfile') and OPNsense.monit.general.logfile|default('') != '' %}
+set logfile {{ OPNsense.monit.general.logfile }}
+{% else %}
+set logfile syslog facility log_daemon
+{% endif %}
+
+{% if helpers.exists('OPNsense.monit.general.statefile') and OPNsense.monit.general.statefile|default('') != '' %}
+set statefile {{ OPNsense.monit.general.statefile }}
+{% endif %}
+
+{% if helpers.exists('OPNsense.monit.general.eventqueuePath') and OPNsense.monit.general.eventqueuePath|default('') != '' %}
+{% set slots = '' %}
+{% if helpers.exists('OPNsense.monit.general.eventqueueSlots') %}
+{% set slots = "slots " ~ OPNsense.monit.general.eventqueueSlots %}
+{% endif %}
+
+set eventqueue basedir {{ OPNsense.monit.general.eventqueuePath }} {{ slots }}
+{% endif %}
+
+{% if helpers.exists('OPNsense.monit.general.mailserver') %}
+{% set port = "port " ~ OPNsense.monit.general.port if OPNsense.monit.general.port|default('') != '' %}
+{% set username = '' %}
+{% set password = '' %}
+{% if helpers.exists('OPNsense.monit.general.username') and helpers.exists('OPNsense.monit.general.password') %}
+{% set username = "username " ~ OPNsense.monit.general.username %}
+{% set password = "password " ~ '"' ~ OPNsense.monit.general.password ~ '"' %}
+{% endif %}
+{% set ssl = 'using ssl' if OPNsense.monit.general.ssl|default('0') == '1' %}
+set mailserver {{ OPNsense.monit.general.mailserver }} {{ port }} {{ username }} {{ password }} {{ ssl }}
+{% endif %}
+
+{% if helpers.exists('OPNsense.monit.alert') %}
+{% for alert in helpers.toList('OPNsense.monit.alert') %}
+{% if alert.enabled|default('0') == '1' %}
+{% set noton = 'not on' if alert.noton|default('0') == '1' %}
+{% set mailformat = "mail-format { " ~ alert.format ~ " }" if alert.format|default('') != '' %}
+{% set reminder = "reminder on " ~ alert.reminder ~ " cycles" if alert.reminder|default('0') != '0' %}
+{% set events = "{ " ~ alert.events ~ " }" if alert.events|default('') != '' %}
+set alert {{ alert.recipient }} {{ noton }} {{ events }} {{ mailformat }} {{ reminder }}
+{% endif %}
+{% endfor %}
+{% endif %}
+
+{% if helpers.exists('OPNsense.monit.service') %}
+{% for service in helpers.toList('OPNsense.monit.service') %}
+{% if service.enabled|default('0') == '1' %}
+{% set pidfile = '' %}
+{% set match = '' %}
+{% set path = '' %}
+{% set address = '' %}
+{% set interface = '' %}
+{% if service.type == 'process' %}
+{% if service.pidfile|default('') != '' %}
+{% set pidfile = "pidfile " ~ service.pidfile %}
+{% elif service.match|default('') != '' %}
+{% set match = "matching " ~ service.match if service.match|default('') != '' %}
+{% endif %}
+check {{ service.type }} {{ service.name }} {{ pidfile }} {{ match }}
+{% elif service.type == 'host' %}
+{% set address = "address " ~ service.address %}
+check {{ service.type }} {{ service.name }} {{ address }}
+{% elif service.type == 'network' %}
+{% if service.address|default('') != '' %}
+{% set address = "address " ~ service.address %}
+{% elif service.interface|default('') != '' %}
+{% set interface = "interface " ~ physical_interface(service.interface) %}
+{% endif %}
+check {{ service.type }} {{ service.name }} {{ address }} {{ interface }}
+{% elif service.type == 'system' %}
+check {{ service.type }} {{ service.name }}
+{% else %}
+{% set path = "with path " ~ service.path %}
+{% if service.type == 'custom' %}
+check program {{ service.name }} {{ path }}
+{% else %}
+check {{ service.type }} {{ service.name }} {{ path }}
+{% endif %}
+{% endif %}
+{% if service.start|default('') != '' %}
+ start program = "{{ service.start }}"
+{% endif %}
+{% if service.start|default('') != '' %}
+ stop program = "{{ service.stop }}"
+{% endif %}
+{% if service.tests is defined %}
+{% for test in service.tests.split(",") %}
+{% set test = helpers.getUUID(test) %}
+{% if test.condition is defined and test.action is defined %}
+{% set epath = '' %}
+{% if test.action == 'exec' and test.path|default('') != '' %}
+{% set epath = test.path %}
+{% endif %}
+ if {{ test.condition }} then {{ test.action }} {{ epath }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endif %}
+
+{% endfor %}
+{% endif %}
+{% endif %}
diff --git a/src/opnsense/service/templates/OPNsense/Monit/rc.conf.d b/src/opnsense/service/templates/OPNsense/Monit/rc.conf.d
new file mode 100644
index 000000000..d19dee084
--- /dev/null
+++ b/src/opnsense/service/templates/OPNsense/Monit/rc.conf.d
@@ -0,0 +1,7 @@
+# DO NOT EDIT THIS FILE -- OPNsense auto-generated file
+{% if helpers.exists('OPNsense.monit.general.enabled') and OPNsense.monit.general.enabled|default("0") == "1" %}
+monit_var_script="/usr/local/opnsense/scripts/OPNsense/Monit/setup.sh"
+monit_enable="YES"
+{% else %}
+monit_enable="NO"
+{% endif %}