201 lines
6.5 KiB
Bash
201 lines
6.5 KiB
Bash
#!/bin/sh
|
|
|
|
# Memory Daemon - Monitors system memory usage and writes to JSON file
|
|
# This daemon only runs when memory monitoring is enabled via settings
|
|
|
|
set -eu
|
|
|
|
# Ensure PATH for OpenWrt/BusyBox
|
|
export PATH="/usr/sbin:/usr/bin:/sbin:/bin:$PATH"
|
|
|
|
# Load centralized logging
|
|
. /www/cgi-bin/services/quecmanager_logger.sh
|
|
|
|
# Configuration
|
|
TMP_DIR="/tmp/quecmanager"
|
|
OUT_JSON="$TMP_DIR/memory.json"
|
|
PID_FILE="$TMP_DIR/memory_daemon.pid"
|
|
CONFIG_FILE="/etc/quecmanager/settings/memory_settings.conf"
|
|
[ -f "$CONFIG_FILE" ] || CONFIG_FILE="/tmp/quecmanager/settings/memory_settings.conf"
|
|
DEFAULT_INTERVAL=1
|
|
SCRIPT_NAME="memory_daemon"
|
|
|
|
# Ensure temp directory exists
|
|
ensure_tmp_dir() {
|
|
[ -d "$TMP_DIR" ] || mkdir -p "$TMP_DIR" || exit 1
|
|
}
|
|
|
|
# Logging function
|
|
log() {
|
|
qm_log_info "daemon" "$SCRIPT_NAME" "$1"
|
|
}
|
|
|
|
# Check if this daemon instance is already running
|
|
daemon_is_running() {
|
|
if [ -f "$PID_FILE" ]; then
|
|
pid="$(cat "$PID_FILE" 2>/dev/null || true)"
|
|
if [ -n "${pid:-}" ] && kill -0 "$pid" 2>/dev/null; then
|
|
# Verify it's actually our daemon by checking process cmdline
|
|
if [ -r "/proc/$pid/cmdline" ] && grep -q "memory_daemon.sh" "/proc/$pid/cmdline" 2>/dev/null; then
|
|
return 0
|
|
else
|
|
# PID file is stale, remove it
|
|
rm -f "$PID_FILE" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
# Write our PID to file
|
|
write_pid() {
|
|
echo "$$" > "$PID_FILE"
|
|
}
|
|
|
|
# Cleanup function
|
|
cleanup() {
|
|
rm -f "$PID_FILE" 2>/dev/null || true
|
|
log "Memory daemon stopped"
|
|
}
|
|
|
|
# Create default config if none exists
|
|
create_default_config() {
|
|
local primary_config="/etc/quecmanager/settings/memory_settings.conf"
|
|
local fallback_config="/tmp/quecmanager/settings/memory_settings.conf"
|
|
|
|
if [ ! -f "$primary_config" ] && [ ! -f "$fallback_config" ]; then
|
|
log "No config file found, creating default configuration"
|
|
|
|
# Try primary location first
|
|
if mkdir -p "/etc/quecmanager/settings" 2>/dev/null; then
|
|
{
|
|
echo "MEMORY_ENABLED=false"
|
|
echo "MEMORY_INTERVAL=1"
|
|
} > "$primary_config" 2>/dev/null && {
|
|
chmod 644 "$primary_config" 2>/dev/null || true
|
|
CONFIG_FILE="$primary_config"
|
|
log "Created default config at $primary_config"
|
|
return 0
|
|
}
|
|
fi
|
|
|
|
# Fallback to tmp location
|
|
mkdir -p "/tmp/quecmanager/settings" 2>/dev/null || true
|
|
{
|
|
echo "MEMORY_ENABLED=false"
|
|
echo "MEMORY_INTERVAL=1"
|
|
} > "$fallback_config" && {
|
|
chmod 644 "$fallback_config" 2>/dev/null || true
|
|
CONFIG_FILE="$fallback_config"
|
|
log "Created default config at $fallback_config"
|
|
return 0
|
|
}
|
|
|
|
log "Failed to create default config file"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Read configuration from file
|
|
read_config() {
|
|
ENABLED="false"
|
|
INTERVAL="$DEFAULT_INTERVAL"
|
|
|
|
if [ -f "$CONFIG_FILE" ]; then
|
|
MEMORY_ENABLED=$(grep -E "^MEMORY_ENABLED=" "$CONFIG_FILE" 2>/dev/null | tail -n1 | cut -d'=' -f2 | tr -d '\r' | tr -d '"')
|
|
MEMORY_INTERVAL=$(grep -E "^MEMORY_INTERVAL=" "$CONFIG_FILE" 2>/dev/null | tail -n1 | cut -d'=' -f2 | tr -d '\r')
|
|
|
|
case "${MEMORY_ENABLED:-}" in
|
|
true|1|on|yes|enabled) ENABLED="true" ;;
|
|
*) ENABLED="false" ;;
|
|
esac
|
|
|
|
if echo "${MEMORY_INTERVAL:-}" | grep -qE '^[0-9]+$'; then
|
|
if [ "$MEMORY_INTERVAL" -ge 1 ] && [ "$MEMORY_INTERVAL" -le 10 ]; then
|
|
INTERVAL="$MEMORY_INTERVAL"
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Write JSON data atomically
|
|
write_json_atomic() {
|
|
local json_data="$1"
|
|
local tmpfile="$(mktemp "$TMP_DIR/memory.XXXXXX" 2>/dev/null || echo "$TMP_DIR/memory.tmp.$$")"
|
|
|
|
if [ -n "$tmpfile" ] && printf '%s' "$json_data" > "$tmpfile" 2>/dev/null; then
|
|
mv "$tmpfile" "$OUT_JSON" 2>/dev/null || {
|
|
# Fallback if move fails
|
|
printf '%s' "$json_data" > "$OUT_JSON" 2>/dev/null || true
|
|
rm -f "$tmpfile" 2>/dev/null || true
|
|
}
|
|
else
|
|
# Direct write fallback
|
|
printf '%s' "$json_data" > "$OUT_JSON" 2>/dev/null || true
|
|
rm -f "$tmpfile" 2>/dev/null || true
|
|
fi
|
|
}
|
|
|
|
# Main execution starts here
|
|
ensure_tmp_dir
|
|
log "Starting memory daemon (PID: $$)"
|
|
|
|
# Check if already running
|
|
if daemon_is_running; then
|
|
log "Memory daemon already running, exiting"
|
|
exit 0
|
|
fi
|
|
|
|
# Create default config if needed
|
|
create_default_config
|
|
|
|
# Set up signal handlers
|
|
trap cleanup EXIT INT TERM
|
|
write_pid
|
|
|
|
# Main monitoring loop
|
|
while true; do
|
|
read_config
|
|
|
|
# Exit if disabled
|
|
if [ "$ENABLED" != "true" ]; then
|
|
log "Memory monitoring disabled in config, exiting"
|
|
exit 0
|
|
fi
|
|
|
|
# Get current timestamp
|
|
ts="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
|
|
# Get memory information using /proc/meminfo (most reliable method)
|
|
if [ -r "/proc/meminfo" ]; then
|
|
# Extract values from /proc/meminfo (values are in kB)
|
|
TOTAL_KB=$(grep "^MemTotal:" /proc/meminfo 2>/dev/null | awk '{print $2}' || echo "0")
|
|
AVAIL_KB=$(grep "^MemAvailable:" /proc/meminfo 2>/dev/null | awk '{print $2}' || echo "0")
|
|
FREE_KB=$(grep "^MemFree:" /proc/meminfo 2>/dev/null | awk '{print $2}' || echo "0")
|
|
|
|
# If MemAvailable is not available (older kernels), estimate it
|
|
if [ "$AVAIL_KB" = "0" ]; then
|
|
CACHED_KB=$(grep "^Cached:" /proc/meminfo 2>/dev/null | awk '{print $2}' || echo "0")
|
|
BUFFERS_KB=$(grep "^Buffers:" /proc/meminfo 2>/dev/null | awk '{print $2}' || echo "0")
|
|
AVAIL_KB=$((FREE_KB + CACHED_KB + BUFFERS_KB))
|
|
fi
|
|
|
|
# Convert to bytes (multiply by 1024)
|
|
TOTAL_BYTES=$((TOTAL_KB * 1024))
|
|
AVAIL_BYTES=$((AVAIL_KB * 1024))
|
|
USED_BYTES=$((TOTAL_BYTES - AVAIL_BYTES))
|
|
|
|
json="{\"total\": $TOTAL_BYTES, \"used\": $USED_BYTES, \"available\": $AVAIL_BYTES, \"timestamp\": \"$ts\"}"
|
|
else
|
|
# Fallback if /proc/meminfo is not available
|
|
log "Warning: /proc/meminfo not readable, using error response"
|
|
json="{\"total\": 0, \"used\": 0, \"available\": 0, \"timestamp\": \"$ts\", \"error\": \"meminfo_unavailable\"}"
|
|
fi
|
|
|
|
# Write the JSON data
|
|
write_json_atomic "$json"
|
|
log "Updated memory data: total=${TOTAL_KB:-0}KB, used=${USED_BYTES:-0}B, available=${AVAIL_KB:-0}KB"
|
|
|
|
# Sleep for the configured interval
|
|
sleep "$INTERVAL"
|
|
done |