Skip to content

Commit ba9cb6c

Browse files
committed
feat: dialogs for setup and maintenance
1 parent 8a19947 commit ba9cb6c

7 files changed

Lines changed: 905 additions & 0 deletions

File tree

‎system/cuos/app.sh‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ HOME="${HOME:-/root}"
1313
export CONFIG_PATH="/system.json"
1414
LAST_CONFIG_PATH="/system_next.json"
1515

16+
RUN_CONFIG="$(jq -r '.run_config' "${CONFIG_PATH}")"
17+
if [[ "${RUN_CONFIG}" = "true" ]]; then
18+
"${SCRIPT_DIR}/dialog-config.sh"
19+
fi
20+
21+
"${SCRIPT_DIR}/dialog-reports.sh" &
22+
1623
action_on_failure() {
1724
report_err "$@"
1825
sleep 60

‎system/cuos/dialog-edit-network.sh‎

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#!/bin/bash
2+
3+
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
4+
5+
export DIALOGRC="${SCRIPT_DIR}/dialog.rc"
6+
7+
CONFIG_PATH="/system.json"
8+
if [[ -n "${TEST}" ]]; then
9+
CONFIG_PATH="system.json"
10+
fi
11+
12+
13+
# Load initial config into a variable
14+
CONFIG_JSON="$(cat "$CONFIG_PATH" || echo '{}')"
15+
CONFIG_NEW="{}"
16+
17+
term() {
18+
if [[ -n "${TEST}" ]]; then "$@"; return; fi
19+
TERM=linux "$@" >/dev/tty1 </dev/tty1
20+
}
21+
22+
cuos_dialog() {
23+
dialog \
24+
--colors \
25+
--no-trim \
26+
--no-collapse \
27+
--cr-wrap \
28+
"$@"
29+
}
30+
31+
# --- Validation functions ---
32+
is_valid_hostname() {
33+
[[ "$1" =~ ^[a-zA-Z][a-zA-Z0-9\.-]+$ ]]
34+
}
35+
36+
is_valid_ip() {
37+
[[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] && \
38+
for octet in $(echo "$1" | tr '.' ' '); do
39+
[[ "$octet" -ge 0 && "$octet" -le 255 ]] || return 1
40+
done
41+
}
42+
43+
is_valid_ips() {
44+
[[ -z "$1" ]] && return 1
45+
for ip in $1; do
46+
is_valid_ip "${ip}" || return 1
47+
done
48+
return 0
49+
}
50+
51+
is_valid_name_or_ip() {
52+
is_valid_hostname "$@" || is_valid_ip "$@"
53+
}
54+
55+
edit() {
56+
local field="$1"
57+
local displayname="$2"
58+
local check_function="$3"
59+
60+
local value
61+
value="$(echo "$CONFIG_JSON" | jq -r "${field} // \"\"")"
62+
while true; do
63+
if ! value="$(term cuos_dialog --inputbox "${displayname}:" 8 40 "${value}" 3>&1 1>&2 2>&3)"; then
64+
return 1
65+
fi
66+
if [[ -z "${check_function}" ]] || "${check_function}" "${value}"; then
67+
CONFIG_NEW="$(echo "${CONFIG_NEW}" | jq --arg val "${value}" "${field}"' = $val')"
68+
CONFIG_JSON="$(echo "${CONFIG_JSON}" | jq --arg val "${value}" "${field}"' = $val')"
69+
return 0
70+
fi
71+
term cuos_dialog --no-cancel --msgbox "Invalid format. Please try again." 8 40
72+
done
73+
}
74+
75+
question() {
76+
local field="$1"
77+
local displayname="$2"
78+
local default="${3:-''}"
79+
80+
local value
81+
value="$(echo "$CONFIG_JSON" | jq -r "${field}")"
82+
if [[ "${value}" == "null" ]]; then
83+
value="${default}"
84+
fi
85+
local choise="--clear" # for nop
86+
if [[ "${value}" != "true" ]]; then
87+
choise="--defaultno"
88+
fi
89+
local newvalue=false
90+
if term cuos_dialog "${choise}" --yesno "${displayname}" 7 40; then
91+
newvalue=true
92+
fi
93+
CONFIG_NEW="$(echo "$CONFIG_NEW" | jq "${field} = ${newvalue}")"
94+
CONFIG_JSON="$(echo "$CONFIG_JSON" | jq "${field} = ${newvalue}")"
95+
96+
[[ "${newvalue}" == "true" ]]
97+
}
98+
99+
MODE="${1:-"all"}"
100+
101+
mode_hostname() {
102+
[[ "${MODE}" == "all" || "${MODE}" == "hostname" ]]
103+
}
104+
mode_network() {
105+
[[ "${MODE}" == "all" || "${MODE}" == "network" ]]
106+
}
107+
108+
109+
if mode_hostname; then
110+
edit '.hostname' "Hostname" is_valid_hostname || exit 1
111+
fi
112+
113+
114+
115+
if mode_network; then
116+
# Number of interfaces
117+
NUM_IFACES=1
118+
119+
# Loop through interfaces
120+
for ((i=0; i<NUM_IFACES; i++)); do
121+
if ! question '.network['"$i"']["dhcp"]' "Configure network via DHCP?" true; then
122+
edit '.network['"$i"']["ip-address"]' "IP address" is_valid_ip || exit 1
123+
edit '.network['"$i"']["network-mask"]' "Network mask" is_valid_ip || exit 1
124+
edit '.network['"$i"']["gateway"]' "Gateway" is_valid_ip || exit 1
125+
edit '.network['"$i"']["dns-server"]' "DNS server(s)" is_valid_ips || exit 1
126+
edit '.network['"$i"']["ntp"]' "NTP server(s)" is_valid_name_or_ip || exit 1
127+
fi
128+
done
129+
130+
if ! term cuos_dialog --clear --yesno "Do you want to save and load the configuration?\n\n${CONFIG_NEW}" 25 50; then
131+
exit 1
132+
fi
133+
fi
134+
135+
# Save to new config
136+
echo "${CONFIG_JSON}" >"${CONFIG_PATH}" || exit 1
137+
138+
if [[ -n "${TEST}" ]]; then exit; fi
139+
140+
if mode_hostname; then
141+
"${SCRIPT_DIR}/init.sh" --reinit set_hostname
142+
fi
143+
if mode_network; then
144+
"${SCRIPT_DIR}/init.sh" --reinit configure_network
145+
fi
146+
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
#!/usr/bin/env bash
2+
# Real system resources report (no API, no installed-state)
3+
# Safe: read-only, uses common tools and /proc
4+
# Prints a structured report to stdout.
5+
#
6+
# Optional: call check_resources_direct_dialog to show in dialog.
7+
8+
set -Euo pipefail
9+
10+
# --- helpers ---------------------------------------------------------
11+
12+
EXCLUDE_IFACES_REGEX="${EXCLUDE_IFACES_REGEX:-^(docker0|docker.*|br-.*|veth.*)$}"
13+
14+
cmd_exists() { command -v "$1" >/dev/null 2>&1; }
15+
16+
fmt_secs() {
17+
# format seconds to d h m s
18+
local s="${1:-0}" d h m
19+
d=$(( s/86400 )); s=$(( s%86400 ))
20+
h=$(( s/3600 )); s=$(( s%3600 ))
21+
m=$(( s/60 )); s=$(( s%60 ))
22+
if (( d > 0 )); then printf "%dd %02dh %02dm %02ds" "$d" "$h" "$m" "$s"
23+
elif (( h > 0 )); then printf "%dh %02dm %02ds" "$h" "$m" "$s"
24+
elif (( m > 0 )); then printf "%dm %02ds" "$m" "$s"
25+
else printf "%ds" "$s"; fi
26+
}
27+
28+
kv_from_meminfo() {
29+
# print value in kB (numeric) for key
30+
local key="$1"
31+
awk -v K="$key" '$1==K":"{print $2}' /proc/meminfo 2>/dev/null
32+
}
33+
34+
cpu_model() {
35+
awk -F: '/^model name[ \t]*:/{gsub(/^[ \t]+/,"",$2); print $2; exit}' /proc/cpuinfo 2>/dev/null
36+
}
37+
38+
cpu_mhz_avg() {
39+
awk -F: '/^cpu MHz[ \t]*:/{sum+=$2; n++} END{if(n>0){printf "%.0f\n", sum/n}}' /proc/cpuinfo 2>/dev/null
40+
}
41+
42+
cpu_cores_count() {
43+
if cmd_exists nproc; then nproc
44+
else awk -F: '/^processor[ \t]*:/{c++} END{print c+0}' /proc/cpuinfo 2>/dev/null
45+
fi
46+
}
47+
48+
loadavg_line() {
49+
if [[ -r /proc/loadavg ]]; then
50+
awk '{print $1" "$2" "$3" (1/5/15 min)"}' /proc/loadavg
51+
else
52+
if cmd_exists uptime; then uptime | sed 's/^.*load average: //'
53+
else echo "n/a"
54+
fi
55+
fi
56+
}
57+
58+
root_df_line() {
59+
df -h -T / 2>/dev/null | awk 'NR==1 || NR==2'
60+
}
61+
62+
df_summary() {
63+
# Exclude pseudo filesystems; show top few
64+
df -h -T -x tmpfs -x devtmpfs -x overlay 2>/dev/null | awk 'NR==1 || NR<=8'
65+
}
66+
67+
list_dns() {
68+
if [[ -f /etc/resolv.conf ]]; then
69+
awk '/^nameserver[ \t]+/{print $2}' /etc/resolv.conf | sed 's/^/ - /'
70+
fi
71+
}
72+
73+
ip_addrs_brief() {
74+
if cmd_exists ip; then
75+
ip -br addr show 2>/dev/null | awk -v R="$EXCLUDE_IFACES_REGEX" '$1 !~ R'
76+
else
77+
echo "ip command not available."
78+
fi
79+
}
80+
81+
ip_links_brief() {
82+
if cmd_exists ip; then
83+
ip -br link show 2>/dev/null | awk -v R="$EXCLUDE_IFACES_REGEX" '$1 !~ R'
84+
else
85+
echo "ip command not available."
86+
fi
87+
}
88+
89+
show_route() {
90+
if cmd_exists ip; then
91+
ip route show 2>/dev/null
92+
fi
93+
}
94+
95+
os_pretty() {
96+
if cmd_exists hostnamectl; then
97+
hostnamectl 2>/dev/null | sed -n '1,8p'
98+
elif [[ -f /etc/os-release ]]; then
99+
. /etc/os-release
100+
echo "Operating System: ${PRETTY_NAME:-unknown}"
101+
echo "Kernel: $(uname -r)"
102+
echo "Architecture: $(uname -m)"
103+
else
104+
echo "Kernel: $(uname -srmo 2>/dev/null || uname -a)"
105+
fi
106+
}
107+
108+
uptime_secs() {
109+
awk '{print int($1)}' /proc/uptime 2>/dev/null || echo 0
110+
}
111+
112+
top_cpu_governor() {
113+
# best-effort; may not exist in VMs/containers
114+
local g
115+
g=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 2>/dev/null || true)
116+
[[ -n "$g" ]] && echo "$g" || echo "n/a"
117+
}
118+
119+
# --- main report -----------------------------------------------------
120+
121+
get_resources() {
122+
# Time and host
123+
local now tz
124+
now="$(date '+%F %T' 2>/dev/null || date)"
125+
tz="$(date '+%Z (%z)' 2>/dev/null || echo "")"
126+
127+
# System section
128+
echo "System Information"
129+
echo "------------------"
130+
echo "Hostname: $(hostname 2>/dev/null || echo n/a)"
131+
os_pretty
132+
echo "Time: $now $tz"
133+
local up; up="$(uptime_secs)"
134+
echo "Uptime: $(fmt_secs "${up:-0}")"
135+
echo
136+
137+
# CPU section
138+
local cores model mhz gov load
139+
cores="$(cpu_cores_count)"
140+
model="$(cpu_model)"; [[ -z "$model" ]] && model="n/a"
141+
mhz="$(cpu_mhz_avg)"; [[ -z "$mhz" ]] && mhz="n/a"
142+
gov="$(top_cpu_governor)"
143+
load="$(loadavg_line)"
144+
echo "CPU"
145+
echo "---"
146+
echo "Model: $model"
147+
echo "Cores: ${cores:-n/a}"
148+
echo "Avg Frequency (MHz): ${mhz}"
149+
echo "Governor: ${gov}"
150+
echo "Load Average: ${load}"
151+
echo
152+
153+
# Memory section (from /proc/meminfo)
154+
local mt mf ma st sf bu ca
155+
mt="$(kv_from_meminfo MemTotal)"
156+
ma="$(kv_from_meminfo MemAvailable)"
157+
st="$(kv_from_meminfo SwapTotal)"
158+
sf="$(kv_from_meminfo SwapFree)"
159+
bu="$(kv_from_meminfo Buffers)"
160+
ca="$(kv_from_meminfo Cached)"
161+
162+
# Convert kB to human MiB/GB approximations
163+
_kb_to_mib() { awk -v k="${1:-0}" 'BEGIN{printf "%.1f", k/1024}'; }
164+
_kb_to_gib() { awk -v k="${1:-0}" 'BEGIN{printf "%.2f", k/1048576}'; }
165+
166+
local used avail pct
167+
if [[ -n "$mt" && -n "$ma" ]]; then
168+
used=$(( mt - ma ))
169+
pct=$(awk -v u="$used" -v t="$mt" 'BEGIN{ if(t>0) printf "%.1f", (u*100.0)/t; else print "n/a"}')
170+
fi
171+
172+
echo "Memory"
173+
echo "------"
174+
if [[ -n "$mt" ]]; then
175+
echo "Total: $(_kb_to_gib "$mt") GiB"
176+
echo "Available: $(_kb_to_gib "${ma:-0}") GiB"
177+
echo "Used (approx = Total-Available): $(_kb_to_gib "${used:-0}") GiB (${pct:-n/a}%)"
178+
echo "Buffers/Cache: $(_kb_to_mib "${bu:-0}") MiB / $(_kb_to_mib "${ca:-0}") MiB"
179+
else
180+
echo "Meminfo not available."
181+
fi
182+
echo "Swap Total: $(_kb_to_gib "${st:-0}") GiB"
183+
echo "Swap Free: $(_kb_to_gib "${sf:-0}") GiB"
184+
echo
185+
186+
# Disk section
187+
echo "Disk Usage"
188+
echo "----------"
189+
echo "(Root filesystem)"
190+
root_df_line
191+
echo
192+
echo "(Top filesystems)"
193+
df_summary
194+
echo
195+
196+
# Network section
197+
echo "Network"
198+
echo "-------"
199+
echo "(Interfaces - brief)"
200+
ip_links_brief
201+
echo
202+
echo "(Addresses - brief)"
203+
ip_addrs_brief
204+
echo
205+
echo "(Routes)"
206+
show_route
207+
echo
208+
echo "(DNS from /etc/resolv.conf)"
209+
list_dns
210+
echo
211+
212+
}
213+
214+
get_resources

0 commit comments

Comments
 (0)