Add web interface button for restart and factory reset

This commit is contained in:
Albrecht Lohofener
2024-02-18 00:18:05 +01:00
parent 89d5212d8e
commit 7453c8eaaa
9 changed files with 114 additions and 307 deletions
+3
View File
@@ -12,11 +12,14 @@ RUN apk add --no-cache \
qemu-hw-usb-host \
qemu-hw-usb-redirect \
nginx \
netcat-openbsd \
&& mkdir -p /usr/share/novnc \
&& wget https://github.com/novnc/noVNC/archive/refs/tags/v${NOVNC_VERSION}.tar.gz -O /tmp/novnc.tar.gz -q \
&& tar -xf /tmp/novnc.tar.gz -C /tmp/ \
&& cd /tmp/noVNC-${NOVNC_VERSION}\
&& mv app core vendor package.json *.html /usr/share/novnc \
&& wget https://github.com/OliveTin/OliveTin/releases/latest/download/OliveTin_linux_arm64.apk \
&& apk add --allow-untrusted OliveTin_linux_arm64.apk \
&& sed -i 's/^worker_processes.*/worker_processes 1;/' /etc/nginx/nginx.conf
# export OPENWRT_VERSION=`date '+%Y%M%W'`_$OPENWRT_VERSION \
+3 -2
View File
@@ -139,7 +139,7 @@ if [ -f /storage/old_version ]; then
chmod +x /mnt/etc/rc.local
umount /mnt
info "Boot previos $OLD_VERSION_ROOTFS to get config"
info "Boot previous $OLD_VERSION_ROOTFS to get config"
qemu-system-aarch64 -M virt \
-m 128 \
-nodefaults \
@@ -220,7 +220,8 @@ exec qemu-system-aarch64 -M virt \
-device qemu-xhci -device usb-kbd \
$LAN_ARGS \
$WAN_ARGS \
$USB_ARGS
$USB_ARGS \
-qmp unix:/run/qmp-sock,server=on,wait=off
# -device virtio-net,netdev=qlan1 -netdev user,id=qlan1,net=192.168.1.0/24,hostfwd=tcp::8000-192.168.1.1:80 \
# -blockdev driver=raw,node-name=hd0,cache.direct=on,file.driver=file,file.filename=/var/vm/openwrt-armsr-armv8-generic-ext4-combined.img \
+56
View File
@@ -0,0 +1,56 @@
#!/bin/sh
# Source: https://www.redhat.com/sysadmin/arguments-options-bash-scripts
VERSION=0.1
############################################################
# Help #
############################################################
Help()
{
# Display Help
echo "Small helper to interact with qemu QMP"
echo
echo "Syntax: qemu_qmp [-r|h|v]"
echo "options:"
echo "r Reset VM."
echo "h Print this Help."
echo "v Print software version and exit."
echo
}
############################################################
############################################################
# Main program #
############################################################
############################################################
############################################################
# Process the input options. Add options as needed. #
############################################################
# Get the options
while getopts ":hvrqf" option; do
case $option in
h) # display Help
Help
exit;;
v) # Show version
echo $VERSION
exit;;
r) # Reset VM
echo -ne '{ "execute": "qmp_capabilities" } { "execute": "system_reset" }' | nc -w 1 -U /run/qmp-sock
exit;;
q) # Quit Qemu
echo -ne '{ "execute": "qmp_capabilities" } { "execute": "quit" }' | nc -w 1 -U /run/qmp-sock
exit;;
f) # Apply factory reset
rm /storage/current_version
rm /storage/rootfs-${OPENWRT_VERSION}.img
echo -ne '{ "execute": "qmp_capabilities" } { "execute": "quit" }' | nc -w 1 -U /run/qmp-sock
exit;;
\?) # Invalid option
echo "Error: Invalid option"
Help
exit;;
esac
done
+4
View File
@@ -168,4 +168,8 @@ cp -r /var/www/* /run/shm
html "Starting $APP for Docker..."
nginx -e stderr
# Start OliveTin
cp -f /var/www/olivetin_config.yaml /etc/OliveTin/config.yaml
OliveTin 2> /var/log/olivetin.log &
return 0
+10 -152
View File
@@ -1,154 +1,12 @@
body {
color: white;
background-color: #125bdb;
font-family: Verdana, Arial, sans-serif;
}
div {
text-align:center;
}
html, body, div, iframe { height: 100%; }
#content {
text-align: center;
padding: 20px;
margin-top: 50px;
}
iframe {
display:inline-block;
margin: 5px auto;
border: solid;
}
footer {
width: 98%;
position: fixed;
bottom: 0px;
height: 40px;
text-align: center;
color: #0c8aeb;
}
#empty {
height: 40px;
/* Same height as footer */
}
a,
a:hover,
a:active,
a:visited {
color: white;
}
footer a:link,
footer a:visited,
footer a:active { color: #0c8aeb; }
footer a:hover { color: #73e6ff; }
.loading:after {
content: " .";
animation: dots 1s steps(5, end) infinite;
}
@keyframes dots {
0%,
20% {
color: rgba(0, 0, 0, 0);
text-shadow: 0.25em 0 0 rgba(0, 0, 0, 0), 0.5em 0 0 rgba(0, 0, 0, 0);
}
40% {
color: white;
text-shadow: 0.25em 0 0 rgba(0, 0, 0, 0), 0.5em 0 0 rgba(0, 0, 0, 0);
}
60% {
text-shadow: 0.25em 0 0 white, 0.5em 0 0 rgba(0, 0, 0, 0);
}
80%,
100% {
text-shadow: 0.25em 0 0 white, 0.5em 0 0 white;
}
}
.spinner_LWk7 {
animation: spinner_GWy6 1.2s linear infinite, spinner_BNNO 1.2s linear infinite
}
.spinner_yOMU {
animation: spinner_GWy6 1.2s linear infinite, spinner_pVqn 1.2s linear infinite;
animation-delay: .15s
}
.spinner_KS4S {
animation: spinner_GWy6 1.2s linear infinite, spinner_6uKB 1.2s linear infinite;
animation-delay: .3s
}
.spinner_zVee {
animation: spinner_GWy6 1.2s linear infinite, spinner_Qw4x 1.2s linear infinite;
animation-delay: .45s
}
@keyframes spinner_GWy6 {
0%,
50% {
width: 9px;
height: 9px
}
10% {
width: 11px;
height: 11px
}
}
@keyframes spinner_BNNO {
0%,
50% {
x: 1.5px;
y: 1.5px
}
10% {
x: .5px;
y: .5px
}
}
@keyframes spinner_pVqn {
0%,
50% {
x: 13.5px;
y: 1.5px
}
10% {
x: 12.5px;
y: .5px
}
}
@keyframes spinner_6uKB {
0%,
50% {
x: 13.5px;
y: 13.5px
}
10% {
x: 12.5px;
y: 12.5px
}
}
@keyframes spinner_Qw4x {
0%,
50% {
x: 1.5px;
y: 13.5px
}
10% {
x: .5px;
y: 12.5px
}
}
+3 -19
View File
@@ -2,33 +2,17 @@
<html>
<head>
[1]
<meta http-equiv="Cache-Control" content="no-cache" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="/css/style.css" />
<link rel="icon" href="/img/favicon.svg" type="image/x-icon">
[2]
</head>
<body>
<div id="page">
<div id="content">
<svg id="spinner" width="64" height="64" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<rect class="spinner_LWk7" fill="#0c8aeb" x="1.5" y="1.5" rx="1" width="9" height="9"/>
<rect class="spinner_yOMU" fill="#0c8aeb" x="13.5" y="1.5" rx="1" width="9" height="9"/>
<rect class="spinner_KS4S" fill="#0c8aeb" x="13.5" y="13.5" rx="1" width="9" height="9"/>
<rect class="spinner_zVee" fill="#0c8aeb" x="1.5" y="13.5" rx="1" width="9" height="9"/>
</svg>
<h1 id="info">[3]</h1>
</div>
<div id="empty">
</div>
<footer id="footer">
[4]<br />
[5]
</footer>
<div>
<iframe src="/olivetin" width="29%"></iframe>
<iframe src="/novnc" width="70%"></iframe>
</div>
<script type="text/javascript" src="/js/script.js"></script>
</body>
</html>
-120
View File
@@ -1,120 +0,0 @@
var request;
var interval = 1000;
function getInfo() {
var url = "/msg.html";
try {
if (window.XMLHttpRequest) {
request = new XMLHttpRequest();
} else {
throw "XMLHttpRequest not available!";
}
request.onreadystatechange = processInfo;
request.open("GET", url, true);
request.send();
} catch (e) {
var err = "Error: " + e.message;
console.log(err);
setError(err);
}
}
function processInfo() {
try {
if (request.readyState != 4) {
return true;
}
var msg = request.responseText;
if (msg == null || msg.length == 0) {
setError("Lost connection");
schedule();
return false;
}
var notFound = (request.status == 404);
if (request.status == 200) {
if (msg.toLowerCase().indexOf("<html>") !== -1) {
notFound = true;
} else {
setInfo(msg);
schedule();
return true;
}
}
if (notFound) {
setInfo("Connecting to VNC", true);
reload();
return true;
}
setError("Error: Received statuscode " + request.status);
schedule();
return false;
} catch (e) {
var err = "Error: " + e.message;
console.log(err);
setError(err);
return false;
}
}
function setInfo(msg, loading, error) {
try {
if (msg == null || msg.length == 0) {
return false;
}
var el = document.getElementById("spinner");
error = !!error;
if (!error) {
el.style.visibility = 'visible';
} else {
el.style.visibility = 'hidden';
}
loading = !!loading;
if (loading) {
msg = "<p class=\"loading\">" + msg + "</p>";
}
el = document.getElementById("info");
if (el.innerHTML != msg) {
el.innerHTML = msg;
}
return true;
} catch (e) {
console.log("Error: " + e.message);
return false;
}
}
function setError(text) {
return setInfo(text, false, true);
}
function schedule() {
setTimeout(getInfo, interval);
}
function reload() {
setTimeout(() => {
document.location.reload();
}, 3000);
}
schedule();
+16 -14
View File
@@ -26,25 +26,17 @@ server {
add_header Cache-Control "no-cache";
location / {
root /run/shm;
if ( -f /run/shm/index.html) {
break;
}
try_files /index.html @vnc;
root /var/www;
}
location @vnc {
root /usr/share/novnc;
location /novnc {
index vnc.html;
alias /usr/share/novnc;
try_files $uri $uri/ /vnc.html;
if ($request_uri = "/") {
return 301 /?resize=scale&autoconnect=true;
if ($request_uri = "/novnc/") {
return 301 ?resize=scale&autoconnect=true&reconnect_delay=2000&reconnect=true;
}
}
location /websockify {
@@ -61,4 +53,14 @@ server {
proxy_pass http://127.0.0.1:5700/;
}
location /olivetin/ {
proxy_pass http://localhost:1337/;
proxy_redirect http://localhost:1337/ http://localhost/OliveTin/;
}
location /websocket {
proxy_set_header Upgrade "websocket";
proxy_set_header Connection "upgrade";
proxy_pass http://localhost:1337/websocket;
}
}
+19
View File
@@ -0,0 +1,19 @@
# There is a built-in micro proxy that will host the webui and REST API all on
# one port (this is called the "Single HTTP Frontend") and means you just need
# one open port in the container/firewalls/etc.
#
# Listen on all addresses available, port 1337
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
# Choose from INFO (default), WARN and DEBUG
logLevel: "INFO"
# Actions (buttons) to show up on the WebUI:
actions:
- title: Restart Openwrt
shell: /run/qemu_qmp.sh -r
icon: restart
- title: Factory reset
shell: /run/qemu_qmp.sh -f
icon: "&#9881;"