diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Core/Api/FirmwareController.php b/src/opnsense/mvc/app/controllers/OPNsense/Core/Api/FirmwareController.php
index 8c3a5ce3faa..925464d809e 100644
--- a/src/opnsense/mvc/app/controllers/OPNsense/Core/Api/FirmwareController.php
+++ b/src/opnsense/mvc/app/controllers/OPNsense/Core/Api/FirmwareController.php
@@ -466,12 +466,17 @@ public function updateAction()
if ($this->request->isPost()) {
$this->getLogger('audit')->notice(sprintf("[Firmware] User %s executed a firmware update", $this->getUserName()));
$backend->configdRun('firmware flush');
- $response['msg_uuid'] = trim($backend->configdRun('firmware update', true));
+
+ $cmd = 'firmware update';
+ if ($this->request->getPost('shutdown') === '1') {
+ $cmd .= ' shutdown';
+ }
+
+ $response['msg_uuid'] = trim($backend->configdRun($cmd, true));
$response['status'] = 'ok';
} else {
$response['status'] = 'failure';
}
-
return $response;
}
@@ -487,12 +492,17 @@ public function upgradeAction()
if ($this->request->isPost()) {
$this->getLogger('audit')->notice(sprintf("[Firmware] User %s executed a firmware upgrade", $this->getUserName()));
$backend->configdRun('firmware flush');
- $response['msg_uuid'] = trim($backend->configdRun('firmware upgrade', true));
+
+ $cmd = 'firmware upgrade';
+ if ($this->request->getPost('shutdown') === '1') {
+ $cmd .= ' shutdown';
+ }
+
+ $response['msg_uuid'] = trim($backend->configdRun($cmd, true));
$response['status'] = 'ok';
} else {
$response['status'] = 'failure';
}
-
return $response;
}
@@ -769,6 +779,8 @@ public function upgradestatusAction()
$result['status'] = 'done';
} elseif (strpos($cmd_result, '***REBOOT***') !== false) {
$result['status'] = 'reboot';
+ } elseif (strpos($cmd_result, '***POWER OFF***') !== false) {
+ $result['status'] = 'shutdown';
}
return $result;
diff --git a/src/opnsense/mvc/app/views/OPNsense/Core/firmware.volt b/src/opnsense/mvc/app/views/OPNsense/Core/firmware.volt
index 7acd66bd63d..9a433a0bdb5 100644
--- a/src/opnsense/mvc/app/views/OPNsense/Core/firmware.volt
+++ b/src/opnsense/mvc/app/views/OPNsense/Core/firmware.volt
@@ -131,8 +131,11 @@
/**
* perform backend action and install poller to update status
*/
- function backend(type) {
+ function backend(type, data) {
$.upgrade_check = type == 'check';
+ if (data === undefined) {
+ data = {};
+ }
$('#update_status').html('');
$('#updatelist').hide();
@@ -140,7 +143,7 @@
$('#updatetab > a').tab('show');
$('#updatetab_progress').addClass("fa fa-spinner fa-pulse");
- ajaxCall('/api/core/firmware/' + type, {}, function () {
+ ajaxCall('/api/core/firmware/' + type, data, function () {
setTimeout(trackStatus, 500);
});
}
@@ -247,21 +250,43 @@
if (major === true) {
reboot_msg = "{{ lang._('The firewall will download all firmware sets and reboot multiple times for this upgrade. All operating system files and packages will be reinstalled as a consequence. This may take several minutes to complete.') }}";
}
+ reboot_msg += '
';
// reboot required, inform the user.
+ let countdownSeconds = 30;
+ let countdownTimer = null;
BootstrapDialog.show({
type:BootstrapDialog.TYPE_WARNING,
title: "{{ lang._('Reboot required') }}",
message: reboot_msg,
+ onshown: function(dialogRef) {
+ let $btn = dialogRef.getButton('btn-reboot');
+ countdownTimer = setInterval(function () {
+ countdownSeconds--;
+ $btn.text('{{ lang._("Confirm") }} (' + countdownSeconds + ')');
+ if (countdownSeconds <= 0) {
+ clearInterval(countdownTimer);
+ $btn.trigger('click');
+ }
+ }, 1000);
+ },
+ onhidden: function() {
+ if (countdownTimer) clearInterval(countdownTimer);
+ },
buttons: [{
- label: "{{ lang._('OK') }}",
+ id: 'btn-reboot',
+ label: '{{ lang._("Confirm") }} (' + countdownSeconds + ')',
cssClass: 'btn-warning',
action: function(dialogRef){
+ if (countdownTimer) clearInterval(countdownTimer);
+ let doShutdown = $('#upgrade_shutdown_cb').is(':checked') ? '1' : '0';
dialogRef.close();
- backend(major === true ? 'upgrade' : 'update');
+ backend(major === true ? 'upgrade' : 'update', {'shutdown': doShutdown});
}
},{
label: "{{ lang._('Cancel') }}",
action: function(dialogRef){
+ if (countdownTimer) clearInterval(countdownTimer);
dialogRef.close();
}
}]
@@ -314,6 +339,14 @@
setTimeout(rebootWait, 45000);
},
});
+ } else if (data['status'] == 'shutdown') {
+ BootstrapDialog.show({
+ type:BootstrapDialog.TYPE_INFO,
+ title: "{{ lang._('Powering Off after update') }}",
+ closable: false,
+ message: "{{ lang._('The system is powering off. You chose to Power Off instead of reboot. The system will need to be started manually.') }}" +
+ ' ',
+ });
} else {
// schedule next poll
setTimeout(trackStatus, 500);
diff --git a/src/opnsense/scripts/firmware/config.sh b/src/opnsense/scripts/firmware/config.sh
index ab0d4af93d4..87e7c6e6520 100755
--- a/src/opnsense/scripts/firmware/config.sh
+++ b/src/opnsense/scripts/firmware/config.sh
@@ -149,6 +149,18 @@ output_done()
exit 0
}
+output_restart_action()
+{
+ KEEP_LOG=${1}
+ PREFER_SHUTDOWN=${2:-0}
+
+ if [ "${PREFER_SHUTDOWN}" = "1" ]; then
+ output_shutdown "${KEEP_LOG}"
+ else
+ output_reboot "${KEEP_LOG}"
+ fi
+}
+
output_reboot()
{
KEEP_LOG=${1}
@@ -163,6 +175,20 @@ output_reboot()
/usr/local/etc/rc.reboot
}
+output_shutdown()
+{
+ KEEP_LOG=${1}
+
+ echo '***POWER OFF***' >> ${LOCKFILE}
+
+ if [ -n "${KEEP_LOG}" ]; then
+ cp ${LOCKFILE} ${LOGFILE}
+ fi
+
+ sleep 5
+ /usr/local/etc/rc.halt
+}
+
# if output is requested clear file and set new request right away
if [ -n "${REQUEST}" ]; then
output_request "${REQUEST}"
diff --git a/src/opnsense/scripts/firmware/update.sh b/src/opnsense/scripts/firmware/update.sh
index 74acde9a5be..0ee26df042f 100755
--- a/src/opnsense/scripts/firmware/update.sh
+++ b/src/opnsense/scripts/firmware/update.sh
@@ -29,6 +29,14 @@ REQUEST="UPDATE"
. /usr/local/opnsense/scripts/firmware/config.sh
+PREFER_SHUTDOWN=0
+for arg in "$@"; do
+ if [ "$arg" = "shutdown" ]; then
+ PREFER_SHUTDOWN=1
+ break
+ fi
+done
+
CMD=${1}
FORCE=
@@ -71,13 +79,13 @@ fi
# if we can update base, we'll do that as well
if opnsense-update ${FORCE} -bk -c; then
if output_cmd opnsense-update ${FORCE} -bk; then
- output_reboot keep-log
+ output_restart_action keep-log "${PREFER_SHUTDOWN}"
fi
fi
if [ "${ALWAYS_REBOOT}" = "1" ]; then
if [ "${PKGS_HASH}" != "$(${PKG} query %n-%v 2> /dev/null | sha256)" ]; then
- output_reboot keep-log
+ output_restart_action keep-log "${PREFER_SHUTDOWN}"
fi
fi
diff --git a/src/opnsense/scripts/firmware/upgrade.sh b/src/opnsense/scripts/firmware/upgrade.sh
index aea4e5733fe..c68c4c0972b 100755
--- a/src/opnsense/scripts/firmware/upgrade.sh
+++ b/src/opnsense/scripts/firmware/upgrade.sh
@@ -29,13 +29,21 @@ REQUEST="UPGRADE"
. /usr/local/opnsense/scripts/firmware/config.sh
+PREFER_SHUTDOWN=0
+for arg in "$@"; do
+ if [ "$arg" = "shutdown" ]; then
+ PREFER_SHUTDOWN=1
+ break
+ fi
+done
+
if output_cmd opnsense-update -u; then
if output_cmd /usr/local/etc/rc.syshook upgrade; then
# pending kernel applies before reboot
if output_cmd opnsense-update -K -c; then
output_cmd opnsense-update -K
fi
- output_reboot keep-log
+ output_restart_action keep-log "${PREFER_SHUTDOWN}"
fi
output_txt "The upgrade was aborted due to an error."
diff --git a/src/opnsense/service/conf/actions.d/actions_firmware.conf b/src/opnsense/service/conf/actions.d/actions_firmware.conf
index 9c6b182e99c..155a019d955 100644
--- a/src/opnsense/service/conf/actions.d/actions_firmware.conf
+++ b/src/opnsense/service/conf/actions.d/actions_firmware.conf
@@ -134,7 +134,7 @@ message:Running firmware update
[upgrade]
command:/usr/sbin/daemon -f /usr/local/opnsense/scripts/firmware/launcher.sh upgrade
-parameters:
+parameters:%s
type:script
message:Running firmware upgrade