Added missing cgi-bin directory

This commit is contained in:
Russel Yasol
2025-03-13 20:51:47 +08:00
parent 54e6407507
commit a0301f4cd2
104 changed files with 46764 additions and 71 deletions

View File

@@ -0,0 +1,26 @@
#!/bin/sh
# Set content type for JSON response
echo "Content-Type: application/json"
echo ""
# Check if the file exists
if [ -f "/etc/config/atcommands.user" ]; then
# Start JSON object
printf "{\n"
awk -F';' '
BEGIN { first = 1 }
{
gsub(/\r/, "", $0)
if (!first) printf ",\n "
else printf " "
gsub(/"/, "\\\"", $1)
gsub(/"/, "\\\"", $2)
printf "\"%s\": \"%s\"", $1, $2
first = 0
}
' /etc/config/atcommands.user
printf "\n}"
else
echo '{"error": "No Data"}'
fi

View File

@@ -0,0 +1,49 @@
#!/bin/sh
echo "Content-type: application/json"
echo ""
# Create a temporary file to store the processed data
temp_file=$(mktemp)
# Process ARP entries and store in temporary file
arp -a | while IFS= read -r line; do
if [ -n "$line" ]; then
# Extract hostname (or IP if hostname is "?"), IP, and MAC
hostname=$(echo "$line" | awk '{print $1}')
ip=$(echo "$line" | awk -F '[()]' '{print $2}')
mac=$(echo "$line" | awk '{print $4}')
# Skip entries without valid MAC addresses
if [ "$mac" = "<incomplete>" ]; then
continue
fi
# If hostname is "?", use the IP address instead
if [ "$hostname" = "?" ]; then
hostname="$ip"
fi
# Store each entry in the temp file
echo "$hostname:$ip:$mac" >> "$temp_file"
fi
done
# Initialize JSON array
echo -n "["
# Process the temporary file to create JSON
first=true
while IFS=: read -r hostname ip mac; do
if [ "$first" = true ]; then
first=false
else
echo -n ","
fi
echo -n "{\"hostname\":\"$hostname\",\"ip\":\"$ip\",\"mac\":\"$mac\"}"
done < "$temp_file"
# Close the JSON array
echo "]"
# Clean up
rm -f "$temp_file"

View File

@@ -0,0 +1,90 @@
#!/bin/sh
echo "Content-type: application/json"
echo ""
mtu_firewall_file="/etc/firewall.user.mtu"
network_interface="rmnet_data0"
lan_utils_script="/etc/data/lanUtils.sh"
get_current_mtu() {
ip link show "$network_interface" | grep -o "mtu [0-9]*" | cut -d' ' -f2
}
update_lanutils_mtu_config() {
local action="$1"
if [ "$action" = "add" ]; then
# Add the MTU firewall file line if not already present
if ! grep -q "local mtu_firewall_file=/etc/firewall.user.mtu" "$lan_utils_script"; then
sed -i '/local ttl_firewall_file=\/etc\/firewall.user.ttl/a local mtu_firewall_file=/etc/firewall.user.mtu' "$lan_utils_script"
fi
elif [ "$action" = "remove" ]; then
# Remove the MTU firewall file line if present
sed -i '/local mtu_firewall_file=\/etc\/firewall.user.mtu/d' "$lan_utils_script"
fi
}
case "$REQUEST_METHOD" in
GET)
# Fetch current MTU
current_mtu=$(get_current_mtu)
current_mtu=${current_mtu:-1500}
# Check if custom MTU is configured
if [ -f "$mtu_firewall_file" ]; then
echo "{\"isEnabled\": true, \"currentValue\": $current_mtu}"
else
echo "{\"isEnabled\": false, \"currentValue\": $current_mtu}"
fi
;;
POST)
read -r post_data
mtu_value=$(echo "$post_data" | sed 's/mtu=//')
# Check for disable functionality
if [ "$mtu_value" = "disable" ]; then
# Remove the MTU configuration file
rm -f "$mtu_firewall_file"
# Remove the MTU configuration line from lanUtils.sh
update_lanutils_mtu_config "remove"
# Get the default MTU
default_mtu=$(get_current_mtu)
default_mtu=${default_mtu:-1500}
echo "{\"success\": true, \"message\": \"MTU configuration disabled\", \"currentValue\": $default_mtu}"
exit 0
fi
# Validate MTU input
if ! [[ "$mtu_value" =~ ^[0-9]+$ ]]; then
echo "{\"success\": false, \"error\": \"Invalid MTU value\"}"
exit 1
fi
# Create firewall MTU configuration file with individual interface commands
> "$mtu_firewall_file" # Clear the file
for iface in $(ls /sys/class/net | grep '^rmnet_data'); do
echo "ip link set $iface mtu $mtu_value" >> "$mtu_firewall_file"
done
# Immediately apply MTU change
for iface in $(ls /sys/class/net | grep '^rmnet_data'); do
ip link set "$iface" mtu "$mtu_value"
done
# Add the MTU configuration line to lanUtils.sh
update_lanutils_mtu_config "add"
# Run lanUtils.sh to update network configuration
if [ -f "$lan_utils_script" ]; then
. "$lan_utils_script"
fi
echo "{\"success\": true, \"message\": \"MTU configuration updated to $mtu_value\", \"currentValue\": $mtu_value}"
;;
*)
echo "{\"success\": false, \"error\": \"Invalid request method\"}"
;;
esac

View File

@@ -0,0 +1,94 @@
#!/bin/sh
echo "Content-type: application/json"
echo ""
ttl_file="/etc/firewall.user.ttl"
lan_utils_script="/etc/data/lanUtils.sh"
setup_persistent_config() {
if [ ! -f "$lan_utils_script" ]; then
echo "{\"success\": false, \"error\": \"lanUtils.sh not found\"}"
return 1
fi
# Backup the original script if not already done
if [ ! -f "${lan_utils_script}.bak" ]; then
cp "$lan_utils_script" "${lan_utils_script}.bak"
fi
# Add the local ttl_firewall_file line if it's not already present
if ! grep -q "local ttl_firewall_file" "$lan_utils_script"; then
sed -i '/local tcpmss_firewall_filev6/a \ local ttl_firewall_file=/etc/firewall.user.ttl' "$lan_utils_script"
fi
# Add the condition to include the ttl_firewall_file if it's not already present
if ! grep -q "if \[ -f \"\$ttl_firewall_file\" \]; then" "$lan_utils_script"; then
sed -i '/if \[ -f "\$tcpmss_firewall_filev6" \]; then/i \ if [ -f "\$ttl_firewall_file" ]; then\n cat \$ttl_firewall_file >> \$firewall_file\n fi' "$lan_utils_script"
fi
}
clear_existing_rules() {
local current_ttl=$1
if [ -n "$current_ttl" ]; then
iptables -t mangle -D POSTROUTING -o rmnet+ -j TTL --ttl-set "$current_ttl" 2>/dev/null
ip6tables -t mangle -D POSTROUTING -o rmnet+ -j HL --hl-set "$current_ttl" 2>/dev/null
fi
}
case "$REQUEST_METHOD" in
GET)
# Ensure consistent JSON format for GET requests
if [ -s "$ttl_file" ]; then
ttl_value=$(grep 'iptables -t mangle -A POSTROUTING' "$ttl_file" | awk '{for(i=1;i<=NF;i++){if($i=="--ttl-set"){print $(i+1)}}}')
# Ensure ttl_value is a number, default to 0 if not
if ! [[ "$ttl_value" =~ ^[0-9]+$ ]]; then
ttl_value=0
fi
echo "{\"isEnabled\": true, \"currentValue\": $ttl_value}"
else
echo "{\"isEnabled\": false, \"currentValue\": 0}"
fi
;;
POST)
read -r post_data
ttl_value=$(echo "$post_data" | sed 's/ttl=//')
# Ensure ttl_file exists
touch "$ttl_file" 2>/dev/null
if [ ! -f "$ttl_file" ]; then
echo "{\"success\": false, \"error\": \"Cannot create TTL file\"}"
exit 1
fi
# Setup persistent configuration
setup_persistent_config
# Get current TTL value for cleanup
current_ttl=$(grep 'iptables -t mangle -A POSTROUTING' "$ttl_file" | awk '{for(i=1;i<=NF;i++){if($i=="--ttl-set"){print $(i+1)}}}')
if ! [[ "$ttl_value" =~ ^[0-9]+$ ]]; then
echo "{\"success\": false, \"error\": \"Invalid TTL value\"}"
elif [ "$ttl_value" = "0" ]; then
clear_existing_rules "$current_ttl"
> "$ttl_file"
echo "{\"success\": true}"
else
# Clear existing rules
clear_existing_rules "$current_ttl"
# Set new rules
echo "iptables -t mangle -A POSTROUTING -o rmnet+ -j TTL --ttl-set $ttl_value" > "$ttl_file"
echo "ip6tables -t mangle -A POSTROUTING -o rmnet+ -j HL --hl-set $ttl_value" >> "$ttl_file"
# Apply the rules
iptables -t mangle -A POSTROUTING -o rmnet+ -j TTL --ttl-set "$ttl_value"
ip6tables -t mangle -A POSTROUTING -o rmnet+ -j HL --hl-set "$ttl_value"
echo "{\"success\": true}"
fi
;;
*)
echo "{\"success\": false, \"error\": \"Invalid request method\"}"
;;
esac

View File

@@ -0,0 +1,266 @@
#!/bin/sh
# AT Queue Client for OpenWRT
# Located in /www/cgi-bin/services/at_queue_client
QUEUE_DIR="/tmp/at_queue"
RESULTS_DIR="$QUEUE_DIR/results"
QUEUE_MANAGER="/www/cgi-bin/services/at_queue_manager.sh"
POLL_INTERVAL=0.01
usage() {
echo "Usage: $0 [options] <AT command>"
echo "Options:"
echo " -w Wait for command completion"
echo " -t Timeout in seconds (default: 240)"
echo " -h Show this help message"
exit 1
}
# Output JSON response
output_json() {
local content="$1"
local headers="${2:-1}" # Default to showing headers
echo "$content"
}
# URL decode function
urldecode() {
local encoded="$1"
logger -t at_queue -p daemon.debug "urldecode: input='$encoded'"
# Handle %2B -> + and %22 -> " conversions
local decoded="${encoded//%2B/+}"
decoded="${decoded//%22/\"}"
# Then handle other encoded characters
decoded=$(printf '%b' "${decoded//%/\\x}")
logger -t at_queue -p daemon.debug "urldecode: output='$decoded'"
echo "$decoded"
}
# Extract command ID from response with improved error handling
get_command_id() {
local response="$1"
echo "DEBUG: Raw response: '$response'" >&2
# Strip any headers from response
local json_response=$(echo "$response" | sed -n '/^{/,$p')
echo "DEBUG: JSON portion: '$json_response'" >&2
# Try to extract command_id using grep and sed instead of jsonfilter
local cmd_id=$(echo "$json_response" | grep -o '"command_id":"[^"]*"' | sed 's/"command_id":"//;s/"$//')
if [ -n "$cmd_id" ]; then
echo "$cmd_id"
return 0
fi
# Fallback to jsonfilter if available
echo "DEBUG: Trying jsonfilter as fallback" >&2
local cmd_id_jsonfilter=$(echo "$json_response" | jsonfilter -e '@.command_id' 2>/dev/null)
if [ -n "$cmd_id_jsonfilter" ]; then
echo "$cmd_id_jsonfilter"
return 0
fi
echo "ERROR: Failed to extract command ID from response" >&2
return 1
}
# Normalize AT command
normalize_at_command() {
local cmd="$1"
logger -t at_queue -p daemon.debug "normalize: input='$cmd'"
# URL decode the command
cmd=$(urldecode "$cmd")
logger -t at_queue -p daemon.debug "normalize: after urldecode='$cmd'"
# Remove any carriage returns or newlines
cmd=$(echo "$cmd" | tr -d '\r\n')
logger -t at_queue -p daemon.debug "normalize: after cleanup='$cmd'"
# Trim leading/trailing whitespace while preserving quotes
cmd=$(echo "$cmd" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
logger -t at_queue -p daemon.debug "normalize: final output='$cmd'"
echo "$cmd"
}
# Submit command with priority handling
submit_command() {
local cmd="$1"
local priority=10
# Set high priority for QSCAN commands for faster processing
if echo "$cmd" | grep -qi "AT+QSCAN"; then
priority=1
fi
# Submit using appropriate method
if [ "${SCRIPT_NAME}" != "" ]; then
# CGI mode - direct execution
local escaped_cmd=$(echo "$cmd" | sed 's/"/\\"/g')
QUERY_STRING="action=enqueue&command=${escaped_cmd}&priority=$priority" "$QUEUE_MANAGER"
else
# CLI mode
"$QUEUE_MANAGER" enqueue "$cmd" "$priority"
fi
}
# Check if result exists with proper error handling
check_result() {
local cmd_id="$1"
local show_headers="${2:-1}" # Add parameter for header control
if [ -f "$RESULTS_DIR/$cmd_id.json" ]; then
local result_content=$(cat "$RESULTS_DIR/$cmd_id.json")
if [ -z "$result_content" ]; then
logger -t at_queue -p daemon.error "Empty result file for command ID: $cmd_id"
local error_json="{\"error\":\"Empty result file\",\"command_id\":\"$cmd_id\"}"
output_json "$error_json" "$show_headers"
return 1
fi
output_json "$result_content" "$show_headers"
return 0
fi
local error_json="{\"error\":\"Result not found\",\"command_id\":\"$cmd_id\"}"
output_json "$error_json" "$show_headers"
return 1
}
# Wait for command completion with optimized polling and better error handling
wait_for_completion() {
local cmd_id="$1"
local timeout="$2"
local show_headers="${3:-1}"
local result_file="$RESULTS_DIR/$cmd_id.json"
if [ -z "$cmd_id" ]; then
local error_json="{\"error\":\"Invalid command ID\"}"
output_json "$error_json" "$show_headers"
return 1
fi
# First quick check
if [ -f "$result_file" ]; then
output_json "$(cat "$result_file")" "$show_headers"
return 0
fi
# Wait with shorter polling interval
local start_time=$(date +%s)
local current_time
while true; do
if [ -f "$result_file" ]; then
output_json "$(cat "$result_file")" "$show_headers"
return 0
fi
current_time=$(date +%s)
if [ $((current_time - start_time)) -ge "$timeout" ]; then
break
fi
sleep $POLL_INTERVAL
done
local error_json=$(cat << EOF
{
"error": "Timeout waiting for completion",
"command_id": "$cmd_id",
"timeout": $timeout
}
EOF
)
output_json "$error_json" "$show_headers"
return 1
}
# CGI request handling
if [ "${SCRIPT_NAME}" != "" ]; then
# Output headers only once at the beginning
echo "Content-Type: application/json"
echo ""
# Parse query string
eval $(echo "$QUERY_STRING" | sed 's/&/;/g')
# Handle different actions
if [ -n "$command_id" ]; then
# Get result for specific command ID
check_result "$command_id" "0" # Don't show headers
elif [ -n "$command" ]; then
# URL decode and normalize the command
command=$(urldecode "$command")
command=$(normalize_at_command "$command")
# Check if it's a valid AT command
if echo "$command" | grep -qi "^AT"; then
# Submit command and get response
response=$(submit_command "$command")
cmd_id=$(get_command_id "$response")
if [ "$wait" = "1" ]; then
if [ -n "$cmd_id" ]; then
wait_for_completion "$cmd_id" "${timeout:-180}" "0" # Don't show headers
else
output_json "{\"error\":\"Failed to get command ID from response\",\"response\":\"$response\"}" "0"
fi
else
output_json "$response" "0" # Don't show headers
fi
else
output_json "{\"error\":\"Invalid AT command format\"}" "0"
fi
else
output_json "{\"error\":\"No command or command_id specified\"}" "0"
fi
exit 0
fi
# CLI processing
wait_mode=0
timeout=180
while getopts "wt:h" opt; do
case $opt in
w) wait_mode=1 ;;
t) timeout="$OPTARG" ;;
h) usage ;;
?) usage ;;
esac
done
shift $((OPTIND-1))
if [ $# -eq 0 ]; then
usage
fi
# Combine remaining arguments into AT command
command="$*"
# Validate AT command format
if ! echo "$command" | grep -qi "^AT"; then
echo "Error: Command must start with 'AT'"
exit 1
fi
# Submit command and get response
response=$(submit_command "$command")
cmd_id=$(get_command_id "$response")
if [ -z "$cmd_id" ]; then
echo "Error: Failed to get command ID"
echo "Response: $response"
exit 1
fi
if [ $wait_mode -eq 1 ]; then
wait_for_completion "$cmd_id" "$timeout"
else
echo "$response"
fi

View File

@@ -0,0 +1,195 @@
#!/bin/sh
# Set content-type for JSON response
echo "Content-type: application/json"
echo ""
# Define paths and constants to match queue system
QUEUE_DIR="/tmp/at_queue"
QUEUE_MANAGER="/www/cgi-bin/services/at_queue_manager"
LOCK_ID="FETCH_DATA_$(date +%s)_$$"
TOKEN_FILE="$QUEUE_DIR/token"
# Logging function (minimized)
log_message() {
# Only log errors and critical info
if [ "$1" = "error" ] || [ "$1" = "crit" ]; then
logger -t at_queue -p "daemon.$1" "$2"
fi
}
# Enhanced JSON string escaping function
escape_json() {
printf '%s' "$1" | awk '
BEGIN { RS="\n"; ORS="\\n" }
{
gsub(/\\/, "\\\\")
gsub(/"/, "\\\"")
gsub(/\r/, "")
gsub(/\t/, "\\t")
gsub(/\f/, "\\f")
gsub(/\b/, "\\b")
print
}
' | sed 's/\\n$//'
}
# Acquire token directly (avoid CGI overhead)
acquire_token() {
local priority="${1:-10}"
local max_attempts=10
local attempt=0
while [ $attempt -lt $max_attempts ]; do
# Check if token file exists
if [ -f "$TOKEN_FILE" ]; then
local current_holder=$(cat "$TOKEN_FILE" | jsonfilter -e '@.id' 2>/dev/null)
local current_priority=$(cat "$TOKEN_FILE" | jsonfilter -e '@.priority' 2>/dev/null)
local timestamp=$(cat "$TOKEN_FILE" | jsonfilter -e '@.timestamp' 2>/dev/null)
local current_time=$(date +%s)
# Check for expired token (> 30 seconds old)
if [ $((current_time - timestamp)) -gt 30 ] || [ -z "$current_holder" ]; then
# Remove expired token
rm -f "$TOKEN_FILE" 2>/dev/null
elif [ $priority -lt $current_priority ]; then
# Preempt lower priority token
rm -f "$TOKEN_FILE" 2>/dev/null
else
# Try again
sleep 0.1
attempt=$((attempt + 1))
continue
fi
fi
# Try to create token file
echo "{\"id\":\"$LOCK_ID\",\"priority\":$priority,\"timestamp\":$(date +%s)}" > "$TOKEN_FILE" 2>/dev/null
chmod 644 "$TOKEN_FILE" 2>/dev/null
# Verify we got the token
local holder=$(cat "$TOKEN_FILE" 2>/dev/null | jsonfilter -e '@.id' 2>/dev/null)
if [ "$holder" = "$LOCK_ID" ]; then
return 0
fi
sleep 0.1
attempt=$((attempt + 1))
done
return 1
}
# Release token directly
release_token() {
# Only remove if it's our token
if [ -f "$TOKEN_FILE" ]; then
local current_holder=$(cat "$TOKEN_FILE" | jsonfilter -e '@.id' 2>/dev/null)
if [ "$current_holder" = "$LOCK_ID" ]; then
rm -f "$TOKEN_FILE" 2>/dev/null
fi
fi
}
# Direct AT command execution with minimal overhead
execute_at_command() {
local CMD="$1"
sms_tool at "$CMD" -t 3 2>/dev/null
}
# Batch process all commands with a single token
process_all_commands() {
local commands="$1"
local priority="${2:-10}"
local first=1
# Acquire a single token for all commands
if ! acquire_token "$priority"; then
log_message "error" "Failed to acquire token for batch processing"
# Return all failed responses
printf '['
first=1
for cmd in $commands; do
[ $first -eq 0 ] && printf ','
first=0
ESCAPED_CMD=$(escape_json "$cmd")
printf '{"command":"%s","response":"Failed to acquire token","status":"error"}' "${ESCAPED_CMD}"
done
printf ']\n'
return 1
fi
# Process all commands with the single token
printf '['
for cmd in $commands; do
[ $first -eq 0 ] && printf ','
first=0
OUTPUT=$(execute_at_command "$cmd")
local CMD_STATUS=$?
ESCAPED_CMD=$(escape_json "$cmd")
ESCAPED_OUTPUT=$(escape_json "$OUTPUT")
if [ $CMD_STATUS -eq 0 ] && [ -n "$OUTPUT" ]; then
printf '{"command":"%s","response":"%s","status":"success"}' \
"${ESCAPED_CMD}" \
"${ESCAPED_OUTPUT}"
else
printf '{"command":"%s","response":"Command failed","status":"error"}' \
"${ESCAPED_CMD}"
fi
done
printf ']\n'
# Release token after all commands are done
release_token
return 0
}
# Main execution with timeout and proper cleanup
trap 'release_token; exit 1' INT TERM
# Command sets
COMMAND_SET_1='AT+QUIMSLOT? AT+CNUM AT+COPS? AT+CIMI AT+ICCID AT+CGSN AT+CPIN? AT+CGDCONT? AT+CREG? AT+CFUN? AT+QENG="servingcell" AT+QTEMP AT+CGCONTRDP AT+QCAINFO AT+QRSRP AT+QMAP="WWAN" AT+C5GREG=2;+C5GREG? AT+CGREG=2;+CGREG? AT+QRSRQ AT+QSINR'
COMMAND_SET_2='AT+CGDCONT? AT+CGCONTRDP AT+QNWPREFCFG="mode_pref" AT+QNWPREFCFG="nr5g_disable_mode" AT+QUIMSLOT? AT+CFUN=?'
COMMAND_SET_3='AT+CGMI AT+CGMM AT+QGMR AT+CNUM AT+CIMI AT+ICCID AT+CGSN AT+QMAP="LANIP" AT+QMAP="WWAN" AT+QGETCAPABILITY'
COMMAND_SET_4='AT+QMAP="MPDN_RULE" AT+QMAP="DHCPV4DNS" AT+QCFG="usbnet"'
COMMAND_SET_5='AT+QRSRP AT+QRSRQ AT+QSINR AT+QCAINFO AT+QSPN'
COMMAND_SET_6='AT+CEREG=2;+CEREG? AT+C5GREG=2;+C5GREG? AT+CPIN? AT+CGDCONT? AT+CGCONTRDP AT+QMAP="WWAN" AT+QRSRP AT+QTEMP AT+QNETRC?'
COMMAND_SET_7='AT+QNWPREFCFG="policy_band" AT+QNWPREFCFG="lte_band";+QNWPREFCFG="nsa_nr5g_band";+QNWPREFCFG="nr5g_band"'
COMMAND_SET_8='AT+QNWLOCK="common/4g" AT+QNWLOCK="common/5g" AT+QNWLOCK="save_ctrl"'
# Get command set from query string with validation
COMMAND_SET=$(echo "$QUERY_STRING" | grep -o 'set=[1-8]' | cut -d'=' -f2 | tr -cd '0-9')
if [ -z "$COMMAND_SET" ] || [ "$COMMAND_SET" -lt 1 ] || [ "$COMMAND_SET" -gt 8 ]; then
COMMAND_SET=1
fi
# Select the appropriate command set
case "$COMMAND_SET" in
1) COMMANDS="$COMMAND_SET_1";;
2) COMMANDS="$COMMAND_SET_2";;
3) COMMANDS="$COMMAND_SET_3";;
4) COMMANDS="$COMMAND_SET_4";;
5) COMMANDS="$COMMAND_SET_5";;
6) COMMANDS="$COMMAND_SET_6";;
7) COMMANDS="$COMMAND_SET_7";;
8) COMMANDS="$COMMAND_SET_8";;
esac
# Set priority based on content
PRIORITY=10
if echo "$COMMANDS" | grep -qi "AT+QSCAN"; then
PRIORITY=1
fi
# Process commands with timeout protection
( sleep 60; kill -TERM $$ 2>/dev/null ) &
TIMEOUT_PID=$!
process_all_commands "$COMMANDS" "$PRIORITY"
# Clean up
kill $TIMEOUT_PID 2>/dev/null
release_token

View File

@@ -0,0 +1,62 @@
#!/bin/sh
# Set Content-Type for CGI script
echo "Content-type: application/json"
echo ""
# Read POST data
read -r POST_DATA
# Debug log for generated hash
DEBUG_LOG="/tmp/auth.log"
# Extract the password from POST data (URL encoded)
USER="root"
INPUT_PASSWORD=$(echo "$POST_DATA" | grep -o 'password=[^&]*' | cut -d= -f2-)
# URL-decode the password while preserving most special characters
# First decode percent-encoded sequences
urldecode() {
local encoded="${1//+/ }"
printf '%b' "${encoded//%/\\x}"
}
# Decode the password
INPUT_PASSWORD=$(urldecode "$INPUT_PASSWORD")
# Basic validation to reject & and $ characters
if echo "$INPUT_PASSWORD" | grep -q '[&$]'; then
echo '{"state":"failed", "message":"Password contains forbidden characters (& or $)"}'
exit 1
fi
# Sanitize the password for shell usage
INPUT_PASSWORD=$(printf '%s' "$INPUT_PASSWORD" | sed 's/[\"]/\\&/g')
# Extract the hashed password from /etc/shadow for the specified user
USER_SHADOW_ENTRY=$(grep "^$USER:" /etc/shadow)
if [ -z "$USER_SHADOW_ENTRY" ]; then
echo '{"state":"failed", "message":"User not found"}'
exit 1
fi
# Extract the password hash (it's the second field, colon-separated)
USER_HASH=$(echo "$USER_SHADOW_ENTRY" | cut -d: -f2)
# Extract the salt (MD5 uses the $1$ prefix followed by the salt)
SALT=$(echo "$USER_HASH" | cut -d'$' -f3)
# Generate a hash from the input password using the same salt
# Use printf to avoid issues with special characters in echo
GENERATED_HASH=$(printf '%s' "$INPUT_PASSWORD" | openssl passwd -1 -salt "$SALT" -stdin)
# Log generated hash for debugging
printf "Generated hash: %s\n" "$GENERATED_HASH" >> "$DEBUG_LOG"
# Compare the generated hash with the one in the shadow file
if [ "$GENERATED_HASH" = "$USER_HASH" ]; then
echo '{"state":"success"}'
else
echo '{"state":"failed", "message":"Authentication failed"}'
fi

View File

@@ -0,0 +1,113 @@
#!/bin/sh
# Set headers for JSON response
echo "Content-type: application/json"
echo ""
# Load UCI functions
. /lib/functions.sh
# Function to safely get UCI value with default
get_uci_value() {
local value
config_get value cell_lock "$1" "$2"
echo "${value:-$2}"
}
# Function to check if daemon is running
check_service_status() {
if [ -f "/var/run/cell_lock_scheduler.pid" ]; then
pid=$(cat /var/run/cell_lock_scheduler.pid 2>/dev/null)
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
echo "running"
return
fi
fi
echo "stopped"
}
# Function to get current status with proper JSON handling
get_current_status() {
local STATUS_FILE="/tmp/cell_lock_status.json"
local status="unknown"
local message="Status not available"
local active="0"
local locked="0"
local timestamp=$(date +%s)
if [ -f "$STATUS_FILE" ]; then
# Try to extract values from status file
if grep -q "status" "$STATUS_FILE"; then
status=$(cat "$STATUS_FILE" | jsonfilter -e '@.status' 2>/dev/null)
# Extract message and remove any surrounding quotes
message=$(cat "$STATUS_FILE" | jsonfilter -e '@.message' 2>/dev/null | sed 's/^"//;s/"$//')
active=$(cat "$STATUS_FILE" | jsonfilter -e '@.active' 2>/dev/null)
locked=$(cat "$STATUS_FILE" | jsonfilter -e '@.locked' 2>/dev/null)
timestamp=$(cat "$STATUS_FILE" | jsonfilter -e '@.timestamp' 2>/dev/null)
fi
fi
# Escape quotes and special characters in message
message=$(echo "$message" | sed 's/\\/\\\\/g; s/"/\\"/g')
# Return the status as a JSON object with properly quoted message
echo "{\"status\":\"$status\",\"message\":\"$message\",\"active\":$active,\"locked\":$locked,\"timestamp\":$timestamp}"
}
# Load configuration
config_load quecmanager
# Check if cell lock section exists
if ! uci -q get quecmanager.cell_lock >/dev/null; then
echo '{"status":"inactive","message":"Cell lock is not configured","enabled":false,"startTime":"","endTime":"","active":false,"locked":false}'
exit 0
fi
# Get service status
service_status=$(check_service_status)
# Get current status
current_status=$(get_current_status)
# Get configuration values
enabled=$(get_uci_value "enabled" "0")
start_time=$(get_uci_value "start_time" "")
end_time=$(get_uci_value "end_time" "")
active=$(get_uci_value "active" "0")
lte_params=$(get_uci_value "lte_params" "")
nr5g_params=$(get_uci_value "nr5g_params" "")
lte_persist=$(get_uci_value "lte_persist" "0")
nr5g_persist=$(get_uci_value "nr5g_persist" "0")
# Convert numeric values to boolean for JSON
enabled_bool="false"
active_bool="false"
locked_bool="false"
[ "$enabled" = "1" ] && enabled_bool="true"
[ "$active" = "1" ] && active_bool="true"
# Get locked status from current_status
locked=$(echo "$current_status" | jsonfilter -e '@.locked' 2>/dev/null)
[ "$locked" = "1" ] && locked_bool="true"
# Extract the message properly from current status
message_value=$(echo "$current_status" | jsonfilter -e '@.message' 2>/dev/null | sed 's/^"//;s/"$//')
# Prepare JSON response in format expected by the component
cat <<EOF
{
"enabled": $enabled_bool,
"start_time": "$start_time",
"end_time": "$end_time",
"active": $active_bool,
"status": "$(echo "$current_status" | jsonfilter -e '@.status')",
"message": "$message_value",
"locked": $locked_bool,
"serviceStatus": "$service_status",
"lteParams": "$lte_params",
"nr5gParams": "$nr5g_params",
"ltePersist": "$lte_persist",
"nr5gPersist": "$nr5g_persist"
}
EOF

View File

@@ -0,0 +1,205 @@
#!/bin/sh
# Set content type to JSON
echo "Content-type: application/json"
echo ""
# Configuration
UCI_CONFIG="quecmanager"
STATUS_FILE="/tmp/cell_lock_status.json"
QUEUE_DIR="/tmp/at_queue"
TOKEN_FILE="$QUEUE_DIR/token"
LOG_DIR="/tmp/log/cell_lock"
LOG_FILE="$LOG_DIR/cell_lock.log"
DEBUG_FILE="$LOG_DIR/debug.log"
# Ensure log directory exists
mkdir -p "$LOG_DIR"
# Function to log messages
log_message() {
local level="${2:-info}"
local message="$1"
local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
# Log to file
echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
# Debug logging
echo "[$timestamp] [$level] $message" >> "$DEBUG_FILE"
# Log to system log
logger -t cell_lock -p "daemon.$level" "$message"
}
# Log request details for debugging
log_request_info() {
echo "==== NEW REQUEST ====" >> "$DEBUG_FILE"
echo "METHOD: $REQUEST_METHOD" >> "$DEBUG_FILE"
echo "QUERY_STRING: $QUERY_STRING" >> "$DEBUG_FILE"
echo "CONTENT_LENGTH: $CONTENT_LENGTH" >> "$DEBUG_FILE"
echo "CONTENT_TYPE: $CONTENT_TYPE" >> "$DEBUG_FILE"
}
# Function to validate time format (HH:MM)
validate_time_format() {
local time="$1"
local name="$2"
if ! echo "$time" | grep -q '^[0-2][0-9]:[0-5][0-9]$'; then
echo "{\"status\":\"error\",\"message\":\"$name must be in format HH:MM (24-hour)\"}"
log_message "$name has invalid format: $time" "error"
return 1
fi
# Further validate hour (00-23)
local hour=$(echo "$time" | cut -d':' -f1)
if [ "$hour" -gt 23 ]; then
echo "{\"status\":\"error\",\"message\":\"Hour in $name must be between 00-23\"}"
log_message "$name has invalid hour: $hour" "error"
return 1
fi
return 0
}
# Log request info for debugging
log_request_info
# Handle GET requests for status
if [ "$REQUEST_METHOD" = "GET" ]; then
log_message "Handling GET request" "debug"
# Load UCI configuration
if [ -f "/etc/config/quecmanager" ]; then
ENABLED=$(uci -q get "$UCI_CONFIG.cell_lock.enabled" || echo "0")
START_TIME=$(uci -q get "$UCI_CONFIG.cell_lock.start_time" || echo "")
END_TIME=$(uci -q get "$UCI_CONFIG.cell_lock.end_time" || echo "")
ACTIVE=$(uci -q get "$UCI_CONFIG.cell_lock.active" || echo "0")
# Convert to JSON boolean format
[ "$ENABLED" = "1" ] && ENABLED="true" || ENABLED="false"
[ "$ACTIVE" = "1" ] && ACTIVE="true" || ACTIVE="false"
# Get current status from status file
STATUS="disabled"
MESSAGE="\"Scheduler is disabled\""
if [ -f "$STATUS_FILE" ]; then
STATUS=$(cat "$STATUS_FILE" | jsonfilter -e '@.status' 2>/dev/null)
MESSAGE=$(cat "$STATUS_FILE" | jsonfilter -e '@.message' 2>/dev/null)
if [ -n "$MESSAGE" ]; then
MESSAGE="\"$MESSAGE\""
else
MESSAGE="\"Status not available\""
fi
fi
# Output JSON response
echo "{\"enabled\":$ENABLED,\"start_time\":\"$START_TIME\",\"end_time\":\"$END_TIME\",\"active\":$ACTIVE,\"status\":\"$STATUS\",\"message\":$MESSAGE}"
log_message "Returned status response" "debug"
else
echo "{\"enabled\":false,\"start_time\":\"\",\"end_time\":\"\",\"active\":false,\"status\":\"unknown\",\"message\":\"Configuration not found\"}"
log_message "No configuration found" "warn"
fi
exit 0
fi
# Handle POST requests for enabling/disabling scheduling
if [ "$REQUEST_METHOD" = "POST" ]; then
log_message "Handling POST request" "debug"
# Read POST data
CONTENT_LENGTH=${CONTENT_LENGTH:-0}
if [ $CONTENT_LENGTH -gt 0 ]; then
POST_DATA=$(dd bs=1 count=$CONTENT_LENGTH 2>/dev/null)
echo "POST_DATA: $POST_DATA" >> "$DEBUG_FILE"
else
POST_DATA=""
echo "No POST_DATA (empty)" >> "$DEBUG_FILE"
fi
# Try to parse JSON data
if [ -n "$POST_DATA" ] && command -v jsonfilter >/dev/null 2>&1; then
log_message "Attempting to parse JSON data" "debug"
# Try to extract values from JSON - allow for differently named fields
ENABLED=$(echo "$POST_DATA" | jsonfilter -e '@.enabled' 2>/dev/null)
if [ -z "$ENABLED" ]; then
ENABLED=$(echo "$POST_DATA" | jsonfilter -e '@.enable' 2>/dev/null)
fi
START_TIME=$(echo "$POST_DATA" | jsonfilter -e '@.startTime' 2>/dev/null)
if [ -z "$START_TIME" ]; then
START_TIME=$(echo "$POST_DATA" | jsonfilter -e '@.start_time' 2>/dev/null)
fi
END_TIME=$(echo "$POST_DATA" | jsonfilter -e '@.endTime' 2>/dev/null)
if [ -z "$END_TIME" ]; then
END_TIME=$(echo "$POST_DATA" | jsonfilter -e '@.end_time' 2>/dev/null)
fi
echo "Parsed JSON: enabled=$ENABLED, start=$START_TIME, end=$END_TIME" >> "$DEBUG_FILE"
# Handle enable/disable logic
if [ "$ENABLED" = "true" ] || [ "$ENABLED" = "1" ]; then
# Validate times for enable request
if [ -z "$START_TIME" ] || [ -z "$END_TIME" ]; then
echo "{\"status\":\"error\",\"message\":\"Start time and end time are required\"}"
log_message "Missing start or end time" "error"
exit 1
fi
# Validate time formats
validate_time_format "$START_TIME" "Start time" || exit 1
validate_time_format "$END_TIME" "End time" || exit 1
# Update configuration
log_message "Enabling scheduling with start=$START_TIME, end=$END_TIME" "info"
uci -q set "$UCI_CONFIG.cell_lock=scheduler"
uci set "$UCI_CONFIG.cell_lock.enabled=1"
uci set "$UCI_CONFIG.cell_lock.start_time=$START_TIME"
uci set "$UCI_CONFIG.cell_lock.end_time=$END_TIME"
uci commit "$UCI_CONFIG"
# Ensure service is running
if [ -x "/etc/init.d/quecmanager_cell_locking" ]; then
/etc/init.d/quecmanager_cell_locking enable
/etc/init.d/quecmanager_cell_locking restart
log_message "Started scheduler service" "info"
else
log_message "Service script not found" "error"
echo "{\"status\":\"error\",\"message\":\"Service script not found\"}"
exit 1
fi
echo "{\"status\":\"success\",\"message\":\"Scheduling enabled\",\"startTime\":\"$START_TIME\",\"endTime\":\"$END_TIME\"}"
log_message "Successfully enabled scheduling" "info"
else
# Disable scheduling
log_message "Disabling scheduling" "info"
uci -q set "$UCI_CONFIG.cell_lock=scheduler"
uci set "$UCI_CONFIG.cell_lock.enabled=0"
uci commit "$UCI_CONFIG"
# Stop service
if [ -x "/etc/init.d/quecmanager_cell_locking" ]; then
/etc/init.d/quecmanager_cell_locking stop
/etc/init.d/quecmanager_cell_locking disable
log_message "Stopped scheduler service" "info"
fi
echo "{\"status\":\"success\",\"message\":\"Scheduling disabled\"}"
log_message "Successfully disabled scheduling" "info"
fi
else
log_message "Failed to parse JSON data or no JSON data received" "error"
echo "{\"status\":\"error\",\"message\":\"Invalid request or missing JSON data\"}"
fi
exit 0
fi
# If no valid method was handled
echo "{\"status\":\"error\",\"message\":\"Invalid request method\"}"
log_message "Invalid request method: $REQUEST_METHOD" "error"
exit 1

View File

@@ -0,0 +1,127 @@
#!/bin/sh
echo "Content-type: application/json"
echo ""
# Initialize error tracking
has_error=false
error_message=""
# Function to append to error message
append_error() {
if [ -z "$error_message" ]; then
error_message="$1"
else
error_message="$error_message; $1"
fi
has_error=true
}
# Function to log cleanup events
log_message() {
local level="$1"
local message="$2"
local LOG_DIR="/tmp/log/apnprofile"
local LOG_FILE="${LOG_DIR}/apnprofile.log"
# Ensure log directory exists
mkdir -p "${LOG_DIR}"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "${timestamp} - [${level}] ${message}" >> "$LOG_FILE"
logger -t apnprofile "${level}: ${message}"
}
log_message "INFO" "Starting APN Profile cleanup process"
# Stop and disable the service
if [ -f "/etc/init.d/apnprofile-service" ]; then
if /etc/init.d/apnprofile-service stop; then
log_message "INFO" "APN Profile service stopped"
else
append_error "Failed to stop APN Profile service"
log_message "ERROR" "Failed to stop APN Profile service"
fi
if /etc/init.d/apnprofile-service disable; then
log_message "INFO" "APN Profile service disabled"
else
append_error "Failed to disable APN Profile service"
log_message "ERROR" "Failed to disable APN Profile service"
fi
# Remove the init.d script
if rm -f "/etc/init.d/apnprofile-service"; then
log_message "INFO" "Removed init.d script"
else
append_error "Failed to remove init.d script"
log_message "ERROR" "Failed to remove init.d script"
fi
fi
# Remove service script
if [ -f "/www/cgi-bin/services/apnprofile.sh" ]; then
if rm -f "/www/cgi-bin/services/apnprofile.sh"; then
log_message "INFO" "Removed service script"
else
append_error "Failed to remove service script"
log_message "ERROR" "Failed to remove service script"
fi
fi
# Remove symlinks in rc.d if they exist
for link in /etc/rc.d/S??apnprofile-service /etc/rc.d/K??apnprofile-service; do
if [ -L "$link" ]; then
if rm -f "$link"; then
log_message "INFO" "Removed rc.d symlink: $link"
else
append_error "Failed to remove rc.d symlink: $link"
log_message "ERROR" "Failed to remove rc.d symlink: $link"
fi
fi
done
# Remove UCI configuration (only removes apn_profile section, leaves other sections intact)
if uci -q get quecmanager.apn_profile >/dev/null; then
if uci delete quecmanager.apn_profile && uci commit quecmanager; then
log_message "INFO" "Removed UCI configuration"
else
append_error "Failed to remove UCI configuration"
log_message "ERROR" "Failed to remove UCI configuration"
fi
fi
# Kill any remaining processes
if pkill -f "/www/cgi-bin/services/apnprofile.sh"; then
log_message "INFO" "Killed remaining APN Profile processes"
fi
# Clean up temporary files
for file in \
"/tmp/at_pipe.txt" \
"/var/run/apnprofile.pid" \
"/tmp/apn_result.txt" \
"/tmp/debug.log" \
"/tmp/inputICCID.txt" \
"/tmp/outputICCID.txt" \
"/tmp/inputAPN.txt" \
"/tmp/outputAPN.txt"
do
if [ -f "$file" ]; then
if rm -f "$file"; then
log_message "INFO" "Removed temporary file: $file"
else
append_error "Failed to remove temporary file: $file"
log_message "ERROR" "Failed to remove temporary file: $file"
fi
fi
done
log_message "INFO" "APN Profile cleanup completed"
# Return appropriate JSON response
if [ "$has_error" = true ]; then
echo "{\"status\": \"error\", \"message\": \"$error_message\"}"
else
echo "{\"status\": \"success\", \"message\": \"APN Profile service successfully removed\"}"
fi

View File

@@ -0,0 +1,144 @@
#!/bin/sh
# Set headers for JSON response
echo "Content-type: application/json"
echo ""
# Load UCI functions
. /lib/functions.sh
# Function to safely get UCI value with default
get_uci_value() {
local value
config_get value apn_profile "$1" "$2"
echo "${value:-$2}"
}
# Function to check if service is running
check_service_status() {
if [ -f "/var/run/apnprofile.pid" ]; then
pid=$(cat /var/run/apnprofile.pid)
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
echo "running"
return
fi
fi
# Double check using process search
if pgrep -f "/www/cgi-bin/services/apnprofile.sh" >/dev/null; then
echo "running"
return
fi
echo "stopped"
}
# Function to get last log entry
get_last_log() {
local LOG_FILE="/tmp/log/apnprofile/apnprofile.log"
if [ -f "$LOG_FILE" ]; then
tail -n 1 "$LOG_FILE"
else
echo "No log entries found"
fi
}
# Function to check if init.d service is enabled
check_service_enabled() {
if [ -f "/etc/init.d/apnprofile-service" ]; then
if /etc/init.d/apnprofile-service enabled; then
echo "true"
return
fi
fi
echo "false"
}
# Load QuecManager configuration
config_load quecmanager
# Check if APN Profile section exists
if ! uci -q get quecmanager.apn_profile >/dev/null; then
echo '{"status": "inactive", "message": "APN Profile service is not configured"}'
exit 0
fi
# Get enabled status from UCI
enabled=$(get_uci_value "enabled" "0")
if [ "$enabled" != "1" ]; then
echo '{"status": "inactive", "message": "APN Profile service is disabled"}'
exit 0
fi
# Check if service script exists
if [ ! -f "/www/cgi-bin/services/apnprofile.sh" ]; then
echo '{"status": "error", "message": "Service script is missing"}'
exit 0
fi
# Get service status information
service_status=$(check_service_status)
service_enabled=$(check_service_enabled)
last_log=$(get_last_log)
# Fetch all configuration values from UCI
iccid_profile1=$(get_uci_value "iccid_profile1" "")
apn_profile1=$(get_uci_value "apn_profile1" "")
pdp_type1=$(get_uci_value "pdp_type1" "")
iccid_profile2=$(get_uci_value "iccid_profile2" "")
apn_profile2=$(get_uci_value "apn_profile2" "")
pdp_type2=$(get_uci_value "pdp_type2" "")
# Function to check if profile data exists
validate_profile_data() {
local iccid="$1"
local apn="$2"
local pdp="$3"
[ -n "$iccid" ] && [ -n "$apn" ] && [ -n "$pdp" ]
}
# Build JSON response
cat <<EOF
{
"status": "active",
"service": {
"status": "${service_status}",
"enabled": ${service_enabled},
"script": "$([ -f "/www/cgi-bin/services/apnprofile.sh" ] && echo "present" || echo "missing")",
"initScript": "$([ -f "/etc/init.d/apnprofile-service" ] && echo "present" || echo "missing")"
},
"profiles": {
EOF
# Add Profile 1 if it exists
if validate_profile_data "$iccid_profile1" "$apn_profile1" "$pdp_type1"; then
cat <<EOF
"profile1": {
"iccid": "${iccid_profile1}",
"apn": "${apn_profile1}",
"pdpType": "${pdp_type1}"
}
EOF
fi
# Add Profile 2 if it exists
if validate_profile_data "$iccid_profile2" "$apn_profile2" "$pdp_type2"; then
# Add comma if Profile 1 was added
[ -n "$iccid_profile1" ] && echo ","
cat <<EOF
"profile2": {
"iccid": "${iccid_profile2}",
"apn": "${apn_profile2}",
"pdpType": "${pdp_type2}"
}
EOF
fi
# Close the profiles object and add last activity
cat <<EOF
},
"lastActivity": "${last_log}"
}
EOF

View File

@@ -0,0 +1,345 @@
#!/bin/sh
# Read POST data
read -r QUERY_STRING
# Function to urldecode
urldecode() {
local value="$1"
value="${value//+/ }"
value="${value//%/\\x}"
printf '%b' "$value"
}
# Extract values from POST data
iccidProfile1=$(echo "$QUERY_STRING" | sed -n 's/.*iccidProfile1=\([^&]*\).*/\1/p' | tr -d "'")
apnProfile1=$(echo "$QUERY_STRING" | sed -n 's/.*apnProfile1=\([^&]*\).*/\1/p' | tr -d "'")
pdpType1=$(echo "$QUERY_STRING" | sed -n 's/.*pdpType1=\([^&]*\).*/\1/p' | tr -d "'")
iccidProfile2=$(echo "$QUERY_STRING" | sed -n 's/.*iccidProfile2=\([^&]*\).*/\1/p' | tr -d "'")
apnProfile2=$(echo "$QUERY_STRING" | sed -n 's/.*apnProfile2=\([^&]*\).*/\1/p' | tr -d "'")
pdpType2=$(echo "$QUERY_STRING" | sed -n 's/.*pdpType2=\([^&]*\).*/\1/p' | tr -d "'")
# URL decode the values
iccidProfile1=$(urldecode "$iccidProfile1")
apnProfile1=$(urldecode "$apnProfile1")
pdpType1=$(urldecode "$pdpType1")
iccidProfile2=$(urldecode "$iccidProfile2")
apnProfile2=$(urldecode "$apnProfile2")
pdpType2=$(urldecode "$pdpType2")
echo "Content-type: application/json"
echo ""
# Validate required first profile
if [ -z "$iccidProfile1" ] || [ -z "$apnProfile1" ] || [ -z "$pdpType1" ]; then
echo '{"status": "error", "message": "Profile 1 is required"}'
exit 1
fi
# Function to log messages
log_message() {
local level="$1"
local message="$2"
local LOG_DIR="/tmp/log/apnprofile"
local LOG_FILE="${LOG_DIR}/apnprofile.log"
mkdir -p "${LOG_DIR}"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "${timestamp} - [${level}] ${message}" >> "$LOG_FILE"
logger -t apnprofile "${level}: ${message}"
}
# Create required directories
mkdir -p /www/cgi-bin/services
mkdir -p /etc/init.d
# Function to create service script
create_service_script() {
cat > /www/cgi-bin/services/apnprofile.sh <<'EOL'
#!/bin/sh
# Load UCI functions
. /lib/functions.sh
# Define file paths
QUEUE_FILE="/tmp/at_pipe.txt"
LOG_DIR="/tmp/log/apnprofile"
LOG_FILE="${LOG_DIR}/apnprofile.log"
PID_FILE="/var/run/apnprofile.pid"
STATE_FILE="/tmp/apnprofile_state.json"
mkdir -p "${LOG_DIR}"
[ ! -f "${QUEUE_FILE}" ] && touch "${QUEUE_FILE}"
# Save PID
echo $$ > "${PID_FILE}"
# Enhanced logging function
log_message() {
local level="$1"
local message="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "${timestamp} - [${level}] ${message}" >> "$LOG_FILE"
logger -t apnprofile "${level}: ${message}"
}
# AT command handling with locking
handle_lock() {
local max_wait=30
local wait_count=0
while [ -f "$QUEUE_FILE" ] && grep -q "AT_COMMAND" "$QUEUE_FILE" && [ $wait_count -lt $max_wait ]; do
sleep 1
wait_count=$((wait_count + 1))
done
printf '{"command":"AT_COMMAND","pid":"%s","timestamp":"%s"}\n' "$$" "$(date '+%H:%M:%S')" >> "$QUEUE_FILE"
}
# Execute AT command with retries
execute_at_command() {
local command="$1"
local result=""
local retry_count=0
local max_retries=3
while [ $retry_count -lt $max_retries ]; do
handle_lock
result=$(sms_tool at "$command" -t 4 2>&1)
local status=$?
sed -i "/\"pid\":\"$$\"/d" "$QUEUE_FILE"
if [ $status -eq 0 ] && [ -n "$result" ]; then
echo "$result"
return 0
fi
retry_count=$((retry_count + 1))
[ $retry_count -lt $max_retries ] && sleep 2
done
return 1
}
# Get current ICCID
get_current_iccid() {
local result=$(execute_at_command "AT+ICCID")
if [ $? -eq 0 ] && echo "$result" | grep -q "+ICCID:"; then
echo "$result" | grep "+ICCID:" | cut -d' ' -f2 | tr -d '[:space:]'
return 0
fi
return 1
}
# Set APN with error handling
set_apn() {
local pdp_type="$1"
local apn="$2"
if [ -z "$pdp_type" ] || [ -z "$apn" ]; then
return 1
fi
if execute_at_command "AT+CGDCONT=1,\"$pdp_type\",\"$apn\";+COPS=2;+COPS=0"; then
return 0
fi
return 1
}
# Function to get current configuration hash
get_config_hash() {
config_load quecmanager
local hash_input=""
# Get Profile 1
config_get ICCID_PROFILE1 apn_profile iccid_profile1
config_get APN_PROFILE1 apn_profile apn_profile1
config_get PDP_TYPE1 apn_profile pdp_type1
# Get Profile 2
config_get ICCID_PROFILE2 apn_profile iccid_profile2
config_get APN_PROFILE2 apn_profile apn_profile2
config_get PDP_TYPE2 apn_profile pdp_type2
hash_input="${ICCID_PROFILE1}${APN_PROFILE1}${PDP_TYPE1}${ICCID_PROFILE2}${APN_PROFILE2}${PDP_TYPE2}"
echo "$hash_input" | md5sum | cut -d' ' -f1
}
# Function to read state file
read_state() {
if [ -f "$STATE_FILE" ]; then
cat "$STATE_FILE"
else
echo "{}"
fi
}
# Function to write state file
write_state() {
local current_iccid="$1"
local config_hash="$2"
local status="$3"
printf '{"iccid":"%s","config_hash":"%s","status":"%s","timestamp":"%s"}' \
"$current_iccid" "$config_hash" "$status" "$(date '+%Y-%m-%d %H:%M:%S')" > "$STATE_FILE"
}
# Main service loop
while true; do
# Get current state
current_state=$(read_state)
current_iccid=$(get_current_iccid)
config_hash=$(get_config_hash)
# Extract values from current state
state_iccid=$(echo "$current_state" | sed -n 's/.*"iccid":"\([^"]*\)".*/\1/p')
state_hash=$(echo "$current_state" | sed -n 's/.*"config_hash":"\([^"]*\)".*/\1/p')
needs_update=0
# Check if update is needed
if [ ! -f "$STATE_FILE" ]; then
log_message "INFO" "No state file found, will apply profile"
needs_update=1
elif [ "$current_iccid" != "$state_iccid" ]; then
log_message "INFO" "ICCID changed from $state_iccid to $current_iccid"
needs_update=1
elif [ "$config_hash" != "$state_hash" ]; then
log_message "INFO" "Configuration changed"
needs_update=1
fi
if [ $needs_update -eq 1 ] && [ -n "$current_iccid" ]; then
config_load quecmanager
# Get Profile 1
config_get ICCID_PROFILE1 apn_profile iccid_profile1
config_get APN_PROFILE1 apn_profile apn_profile1
config_get PDP_TYPE1 apn_profile pdp_type1
# Get Profile 2
config_get ICCID_PROFILE2 apn_profile iccid_profile2
config_get APN_PROFILE2 apn_profile apn_profile2
config_get PDP_TYPE2 apn_profile pdp_type2
if [ "${current_iccid}" = "${ICCID_PROFILE1}" ]; then
if set_apn "$PDP_TYPE1" "$APN_PROFILE1"; then
log_message "INFO" "Successfully applied Profile 1"
write_state "$current_iccid" "$config_hash" "success"
else
log_message "ERROR" "Failed to apply Profile 1"
write_state "$current_iccid" "$config_hash" "error"
fi
elif [ -n "$ICCID_PROFILE2" ] && [ "${current_iccid}" = "${ICCID_PROFILE2}" ]; then
if set_apn "$PDP_TYPE2" "$APN_PROFILE2"; then
log_message "INFO" "Successfully applied Profile 2"
write_state "$current_iccid" "$config_hash" "success"
else
log_message "ERROR" "Failed to apply Profile 2"
write_state "$current_iccid" "$config_hash" "error"
fi
else
log_message "INFO" "No matching ICCID profile found"
write_state "$current_iccid" "$config_hash" "no_match"
fi
fi
sleep 10
done
EOL
chmod 755 /www/cgi-bin/services/apnprofile.sh
}
# Function to create init.d script
create_init_script() {
cat > /etc/init.d/apnprofile-service <<'EOL'
#!/bin/sh /etc/rc.common
START=99
STOP=10
USE_PROCD=1
start_service() {
local enabled
# Check if service is enabled in UCI
config_load quecmanager
config_get enabled apn_profile enabled '0'
[ "$enabled" != "1" ] && return 0
procd_open_instance
procd_set_param command /www/cgi-bin/services/apnprofile.sh
procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}
procd_set_param stdout 1
procd_set_param stderr 1
procd_set_param nice 19
procd_close_instance
}
service_triggers() {
procd_add_reload_trigger "quecmanager"
}
reload_service() {
stop
start
}
EOL
chmod 755 /etc/init.d/apnprofile-service
}
# Initialize UCI configuration
touch /etc/config/quecmanager
# Remove existing APN profile section if it exists
uci -q delete quecmanager.apn_profile
# Create new APN profile section
uci set quecmanager.apn_profile=service
uci set quecmanager.apn_profile.enabled=1
# Set Profile 1 configuration
uci set quecmanager.apn_profile.iccid_profile1="$iccidProfile1"
uci set quecmanager.apn_profile.apn_profile1="$apnProfile1"
uci set quecmanager.apn_profile.pdp_type1="$pdpType1"
# Set Profile 2 configuration if provided
if [ -n "$iccidProfile2" ]; then
uci set quecmanager.apn_profile.iccid_profile2="$iccidProfile2"
uci set quecmanager.apn_profile.apn_profile2="$apnProfile2"
uci set quecmanager.apn_profile.pdp_type2="$pdpType2"
fi
# Commit UCI changes
if ! uci commit quecmanager; then
log_message "ERROR" "Failed to save UCI configuration"
echo '{"status": "error", "message": "Failed to save UCI configuration"}'
exit 1
fi
log_message "INFO" "UCI configuration saved successfully"
# Create service script if it doesn't exist
if [ ! -f "/www/cgi-bin/services/apnprofile.sh" ]; then
create_service_script
log_message "INFO" "Created service script"
fi
# Create init.d script if it doesn't exist
if [ ! -f "/etc/init.d/apnprofile-service" ]; then
create_init_script
log_message "INFO" "Created init.d script"
fi
# Enable and start the service
/etc/init.d/apnprofile-service enable
if /etc/init.d/apnprofile-service restart; then
log_message "INFO" "Service started successfully"
echo '{"status": "success", "message": "APN profiles saved and service started"}'
else
log_message "ERROR" "Failed to start service"
echo '{"status": "error", "message": "Failed to start service"}'
fi

View File

@@ -0,0 +1,127 @@
#!/bin/sh
echo "Content-type: application/json"
echo ""
# Initialize error tracking
has_error=false
error_message=""
# Function to append to error message
append_error() {
if [ -z "$error_message" ]; then
error_message="$1"
else
error_message="$error_message; $1"
fi
has_error=true
}
# Function to log cleanup events
log_message() {
local level="$1"
local message="$2"
local LOG_DIR="/tmp/log/imeiprofile"
local LOG_FILE="${LOG_DIR}/imeiprofile.log"
# Ensure log directory exists
mkdir -p "${LOG_DIR}"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "${timestamp} - [${level}] ${message}" >> "$LOG_FILE"
logger -t imeiprofile "${level}: ${message}"
}
log_message "INFO" "Starting IMEI Profile cleanup process"
# Stop and disable the service
if [ -f "/etc/init.d/imeiprofile-service" ]; then
if /etc/init.d/imeiprofile-service stop; then
log_message "INFO" "IMEI Profile service stopped"
else
append_error "Failed to stop IMEI Profile service"
log_message "ERROR" "Failed to stop IMEI Profile service"
fi
if /etc/init.d/imeiprofile-service disable; then
log_message "INFO" "IMEI Profile service disabled"
else
append_error "Failed to disable IMEI Profile service"
log_message "ERROR" "Failed to disable IMEI Profile service"
fi
# Remove the init.d script
if rm -f "/etc/init.d/imeiprofile-service"; then
log_message "INFO" "Removed init.d script"
else
append_error "Failed to remove init.d script"
log_message "ERROR" "Failed to remove init.d script"
fi
fi
# Remove service script
if [ -f "/www/cgi-bin/services/imeiprofile.sh" ]; then
if rm -f "/www/cgi-bin/services/imeiprofile.sh"; then
log_message "INFO" "Removed service script"
else
append_error "Failed to remove service script"
log_message "ERROR" "Failed to remove service script"
fi
fi
# Remove symlinks in rc.d if they exist
for link in /etc/rc.d/S??imeiprofile-service /etc/rc.d/K??imeiprofile-service; do
if [ -L "$link" ]; then
if rm -f "$link"; then
log_message "INFO" "Removed rc.d symlink: $link"
else
append_error "Failed to remove rc.d symlink: $link"
log_message "ERROR" "Failed to remove rc.d symlink: $link"
fi
fi
done
# Remove UCI configuration
if uci -q get quecmanager.imei_profile >/dev/null; then
if uci delete quecmanager.imei_profile && uci commit quecmanager; then
log_message "INFO" "Removed UCI configuration"
else
append_error "Failed to remove UCI configuration"
log_message "ERROR" "Failed to remove UCI configuration"
fi
fi
# Kill any remaining processes
if pkill -f "/www/cgi-bin/services/imeiprofile.sh"; then
log_message "INFO" "Killed remaining IMEI Profile processes"
fi
# Clean up temporary files
for file in \
"/tmp/at_pipe.txt" \
"/var/run/imeiprofile.pid" \
"/tmp/imei_result.txt" \
"/tmp/debug.log" \
"/tmp/inputICCID.txt" \
"/tmp/outputICCID.txt" \
"/tmp/inputIMEI.txt" \
"/tmp/outputIMEI.txt"
do
if [ -f "$file" ]; then
if rm -f "$file"; then
log_message "INFO" "Removed temporary file: $file"
else
append_error "Failed to remove temporary file: $file"
log_message "ERROR" "Failed to remove temporary file: $file"
fi
fi
done
log_message "INFO" "IMEI Profile cleanup completed"
# Return appropriate JSON response
if [ "$has_error" = true ]; then
echo "{\"status\": \"error\", \"message\": \"$error_message\"}"
else
echo "{\"status\": \"success\", \"message\": \"IMEI Profile service successfully removed\"}"
fi

View File

@@ -0,0 +1,138 @@
#!/bin/sh
# Set headers for JSON response
echo "Content-type: application/json"
echo ""
# Load UCI functions
. /lib/functions.sh
# Function to safely get UCI value with default
get_uci_value() {
local value
config_get value imei_profile "$1" "$2"
echo "${value:-$2}"
}
# Function to check if service is running
check_service_status() {
if [ -f "/var/run/imeiprofile.pid" ]; then
pid=$(cat /var/run/imeiprofile.pid)
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
echo "running"
return
fi
fi
# Double check using process search
if pgrep -f "/www/cgi-bin/services/imeiprofile.sh" >/dev/null; then
echo "running"
return
fi
echo "stopped"
}
# Function to get last log entry
get_last_log() {
local LOG_FILE="/tmp/log/imeiprofile/imeiprofile.log"
if [ -f "$LOG_FILE" ]; then
tail -n 1 "$LOG_FILE"
else
echo "No log entries found"
fi
}
# Function to check if init.d service is enabled
check_service_enabled() {
if [ -f "/etc/init.d/imeiprofile-service" ]; then
if /etc/init.d/imeiprofile-service enabled; then
echo "true"
return
fi
fi
echo "false"
}
# Load QuecManager configuration
config_load quecmanager
# Check if IMEI Profile section exists
if ! uci -q get quecmanager.imei_profile >/dev/null; then
echo '{"status": "inactive", "message": "IMEI Profile service is not configured"}'
exit 0
fi
# Get enabled status from UCI
enabled=$(get_uci_value "enabled" "0")
if [ "$enabled" != "1" ]; then
echo '{"status": "inactive", "message": "IMEI Profile service is disabled"}'
exit 0
fi
# Check if service script exists
if [ ! -f "/www/cgi-bin/services/imeiprofile.sh" ]; then
echo '{"status": "error", "message": "Service script is missing"}'
exit 0
fi
# Get service status information
service_status=$(check_service_status)
service_enabled=$(check_service_enabled)
last_log=$(get_last_log)
# Fetch configuration values from UCI
iccid_profile1=$(get_uci_value "iccid_profile1" "")
imei_profile1=$(get_uci_value "imei_profile1" "")
iccid_profile2=$(get_uci_value "iccid_profile2" "")
imei_profile2=$(get_uci_value "imei_profile2" "")
# Function to check if profile data exists
validate_profile_data() {
local iccid="$1"
local imei="$2"
[ -n "$iccid" ] && [ -n "$imei" ]
}
# Build JSON response
cat <<EOF
{
"status": "active",
"service": {
"status": "${service_status}",
"enabled": ${service_enabled},
"script": "$([ -f "/www/cgi-bin/services/imeiprofile.sh" ] && echo "present" || echo "missing")",
"initScript": "$([ -f "/etc/init.d/imeiprofile-service" ] && echo "present" || echo "missing")"
},
"profiles": {
EOF
# Add Profile 1 if it exists
if validate_profile_data "$iccid_profile1" "$imei_profile1"; then
cat <<EOF
"profile1": {
"iccid": "${iccid_profile1}",
"imei": "${imei_profile1}"
}
EOF
fi
# Add Profile 2 if it exists
if validate_profile_data "$iccid_profile2" "$imei_profile2"; then
# Add comma if Profile 1 was added
[ -n "$iccid_profile1" ] && echo ","
cat <<EOF
"profile2": {
"iccid": "${iccid_profile2}",
"imei": "${imei_profile2}"
}
EOF
fi
# Close the profiles object and add last activity
cat <<EOF
},
"lastActivity": "${last_log}"
}
EOF

View File

@@ -0,0 +1,291 @@
#!/bin/sh
# Read POST data
read -r QUERY_STRING
# Function to urldecode
urldecode() {
local value="$1"
value="${value//+/ }"
value="${value//%/\\x}"
printf '%b' "$value"
}
# Extract values from POST data
iccidProfile1=$(echo "$QUERY_STRING" | sed -n 's/.*iccidProfile1=\([^&]*\).*/\1/p' | tr -d "'")
imeiProfile1=$(echo "$QUERY_STRING" | sed -n 's/.*imeiProfile1=\([^&]*\).*/\1/p' | tr -d "'")
iccidProfile2=$(echo "$QUERY_STRING" | sed -n 's/.*iccidProfile2=\([^&]*\).*/\1/p' | tr -d "'")
imeiProfile2=$(echo "$QUERY_STRING" | sed -n 's/.*imeiProfile2=\([^&]*\).*/\1/p' | tr -d "'")
# URL decode the values
iccidProfile1=$(urldecode "$iccidProfile1")
imeiProfile1=$(urldecode "$imeiProfile1")
iccidProfile2=$(urldecode "$iccidProfile2")
imeiProfile2=$(urldecode "$imeiProfile2")
echo "Content-type: application/json"
echo ""
# Validate required first profile
if [ -z "$iccidProfile1" ] || [ -z "$imeiProfile1" ]; then
echo '{"status": "error", "message": "Profile 1 is required"}'
exit 1
fi
# Function to log messages
log_message() {
local level="$1"
local message="$2"
local LOG_DIR="/tmp/log/imeiprofile"
local LOG_FILE="${LOG_DIR}/imeiprofile.log"
mkdir -p "${LOG_DIR}"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "${timestamp} - [${level}] ${message}" >> "$LOG_FILE"
logger -t imeiprofile "${level}: ${message}"
}
# Create required directories
mkdir -p /www/cgi-bin/services
# Function to create service script
create_service_script() {
cat > /www/cgi-bin/services/imeiprofile.sh <<'EOL'
#!/bin/sh
# Load UCI functions
. /lib/functions.sh
# Define file paths
QUEUE_FILE="/tmp/at_pipe.txt"
LOG_DIR="/tmp/log/imeiprofile"
LOG_FILE="${LOG_DIR}/imeiprofile.log"
PID_FILE="/var/run/imeiprofile.pid"
mkdir -p "${LOG_DIR}"
[ ! -f "${QUEUE_FILE}" ] && touch "${QUEUE_FILE}"
# Save PID
echo $$ > "${PID_FILE}"
# Enhanced logging function
log_message() {
local level="$1"
local message="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "${timestamp} - [${level}] ${message}" >> "$LOG_FILE"
logger -t imeiprofile "${level}: ${message}"
}
# AT command handling with locking
handle_lock() {
local max_wait=30
local wait_count=0
while [ -f "$QUEUE_FILE" ] && grep -q "AT_COMMAND" "$QUEUE_FILE" && [ $wait_count -lt $max_wait ]; do
sleep 1
wait_count=$((wait_count + 1))
done
printf '{"command":"AT_COMMAND","pid":"%s","timestamp":"%s"}\n' "$$" "$(date '+%H:%M:%S')" >> "$QUEUE_FILE"
}
# Execute AT command with retries
execute_at_command() {
local command="$1"
local result=""
local retry_count=0
local max_retries=3
while [ $retry_count -lt $max_retries ]; do
handle_lock
result=$(sms_tool at "$command" -t 4 2>&1)
local status=$?
sed -i "/\"pid\":\"$$\"/d" "$QUEUE_FILE"
if [ $status -eq 0 ] && [ -n "$result" ]; then
echo "$result"
return 0
fi
retry_count=$((retry_count + 1))
[ $retry_count -lt $max_retries ] && sleep 2
done
return 1
}
# Get current ICCID
get_current_iccid() {
local result=$(execute_at_command "AT+ICCID")
if [ $? -eq 0 ] && echo "$result" | grep -q "+ICCID:"; then
echo "$result" | grep "+ICCID:" | cut -d' ' -f2 | tr -d '[:space:]'
return 0
fi
return 1
}
# Get current IMEI
get_current_imei() {
local result=$(execute_at_command "AT+CGSN")
if [ $? -eq 0 ]; then
echo "$result" | grep -v "AT+CGSN" | grep -v "OK" | tr -d '\r\n[:space:]'
return 0
fi
return 1
}
# Set IMEI
set_imei() {
local imei="$1"
if execute_at_command "AT+EGMR=1,7,\"$imei\";+QPOWD=1"; then
return 0
fi
return 1
}
# Function to safely get UCI value with default
get_uci_value() {
local value
config_get value imei_profile "$1" "$2"
echo "${value:-$2}"
}
# Main service loop
while true; do
# Load current configuration
config_load quecmanager
# Get Profile 1
iccid_profile1=$(get_uci_value "iccid_profile1")
imei_profile1=$(get_uci_value "imei_profile1")
# Get Profile 2
iccid_profile2=$(get_uci_value "iccid_profile2")
imei_profile2=$(get_uci_value "imei_profile2")
# Get current ICCID and IMEI
current_iccid=$(get_current_iccid)
current_imei=$(get_current_imei)
if [ $? -eq 0 ] && [ -n "$current_iccid" ] && [ -n "$current_imei" ]; then
if [ "${current_iccid}" = "${iccid_profile1}" ]; then
if [ "${current_imei}" != "${imei_profile1}" ]; then
if set_imei "${imei_profile1}"; then
log_message "INFO" "Successfully applied Profile 1 IMEI"
else
log_message "ERROR" "Failed to apply Profile 1 IMEI"
fi
fi
elif [ -n "$iccid_profile2" ] && [ "${current_iccid}" = "${iccid_profile2}" ]; then
if [ "${current_imei}" != "${imei_profile2}" ]; then
if set_imei "${imei_profile2}"; then
log_message "INFO" "Successfully applied Profile 2 IMEI"
else
log_message "ERROR" "Failed to apply Profile 2 IMEI"
fi
fi
else
log_message "INFO" "No matching ICCID profile found"
fi
else
log_message "ERROR" "Failed to get current ICCID or IMEI"
fi
sleep 30
done
EOL
chmod 755 /www/cgi-bin/services/imeiprofile.sh
}
# Function to create init.d script
create_init_script() {
cat > /etc/init.d/imeiprofile-service <<'EOL'
#!/bin/sh /etc/rc.common
START=99
STOP=10
USE_PROCD=1
start_service() {
local enabled
# Check if service is enabled in UCI
config_load quecmanager
config_get enabled imei_profile enabled '0'
[ "$enabled" != "1" ] && return 0
procd_open_instance
procd_set_param command /www/cgi-bin/services/imeiprofile.sh
procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}
procd_set_param stdout 1
procd_set_param stderr 1
procd_set_param nice 19
procd_close_instance
}
service_triggers() {
procd_add_reload_trigger "quecmanager"
}
reload_service() {
stop
start
}
EOL
chmod 755 /etc/init.d/imeiprofile-service
}
# Initialize UCI configuration
touch /etc/config/quecmanager
# Remove existing IMEI profile section if it exists
uci -q delete quecmanager.imei_profile
# Create new IMEI profile section
uci set quecmanager.imei_profile=service
uci set quecmanager.imei_profile.enabled=1
# Set Profile 1 configuration
uci set quecmanager.imei_profile.iccid_profile1="$iccidProfile1"
uci set quecmanager.imei_profile.imei_profile1="$imeiProfile1"
# Set Profile 2 configuration if provided
if [ -n "$iccidProfile2" ]; then
uci set quecmanager.imei_profile.iccid_profile2="$iccidProfile2"
uci set quecmanager.imei_profile.imei_profile2="$imeiProfile2"
fi
# Commit UCI changes
if ! uci commit quecmanager; then
log_message "ERROR" "Failed to save UCI configuration"
echo '{"status": "error", "message": "Failed to save UCI configuration"}'
exit 1
fi
log_message "INFO" "UCI configuration saved successfully"
# Create service script if it doesn't exist
if [ ! -f "/www/cgi-bin/services/imeiprofile.sh" ]; then
create_service_script
log_message "INFO" "Created service script"
fi
# Create init.d script if it doesn't exist
if [ ! -f "/etc/init.d/imeiprofile-service" ]; then
create_init_script
log_message "INFO" "Created init.d script"
fi
# Enable and start the service
/etc/init.d/imeiprofile-service enable
if /etc/init.d/imeiprofile-service restart; then
log_message "INFO" "Service started successfully"
echo '{"status": "success", "message": "IMEI profiles saved and service started"}'
else
log_message "ERROR" "Failed to start service"
echo '{"status": "error", "message": "Failed to start service"}'
fi

View File

@@ -0,0 +1,262 @@
#!/bin/sh
# Configuration
CONFIG_FILE="/etc/cell_lock_schedule.conf"
STATUS_FILE="/tmp/cell_lock_status"
CELL_LOCK_SCRIPT="/usr/bin/set_cell_lock.sh"
QUEUE_FILE="/tmp/at_pipe.txt"
LOG_FILE="/tmp/cell_lock.log"
# Function to log messages
log_message() {
local level="$1"
local message="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "${timestamp} - [${level}] ${message}" >> "$LOG_FILE"
logger -t cell_lock "${level}: ${message}"
}
# Function to handle AT command queue
handle_lock() {
log_message "DEBUG" "Checking queue file status before lock"
if [ ! -f "$QUEUE_FILE" ]; then
log_message "DEBUG" "Queue file does not exist, creating it"
touch "$QUEUE_FILE"
fi
# Clean any stale entries
if grep -q "\"command\":\"AT_COMMAND\"" "$QUEUE_FILE"; then
local wait_count=0
while [ $wait_count -lt 6 ]; do
if ! grep -q "\"command\":\"AT_COMMAND\"" "$QUEUE_FILE"; then
break
fi
sleep 1
wait_count=$((wait_count + 1))
done
[ $wait_count -eq 6 ] && sed -i "/\"command\":\"AT_COMMAND\"/d" "$QUEUE_FILE"
fi
printf '{"command":"AT_COMMAND","pid":"%s","timestamp":"%s"}\n' \
"$$" \
"$(date '+%H:%M:%S')" >> "$QUEUE_FILE"
}
# Function to execute AT command
execute_at_command() {
local command="$1"
local result=""
log_message "DEBUG" "Executing AT command: ${command}"
handle_lock
result=$(sms_tool at "$command" -t 4 2>&1)
local status=$?
sed -i "/\"pid\":\"$$\"/d" "$QUEUE_FILE"
if [ $status -ne 0 ]; then
log_message "ERROR" "Command failed with status $status: $command"
log_message "ERROR" "Command output: $result"
return 1
fi
log_message "DEBUG" "Command successful. Output: $result"
echo "$result"
return 0
}
# Function to create set_cell_lock.sh script
create_cell_lock_script() {
if [ ! -f "$CELL_LOCK_SCRIPT" ]; then
cat >"$CELL_LOCK_SCRIPT" <<'EOL'
#!/bin/sh
ACTION=$1
LTE_PARAMS=$2
NR5G_PARAMS=$3
QUEUE_FILE="/tmp/at_pipe.txt"
LOG_FILE="/tmp/cell_lock.log"
# Import common functions
. /etc/quecmanager/imei_profile/common_functions.sh || {
echo "Failed to import common functions"
exit 1
}
case "$ACTION" in
enable)
# Enable LTE lock if parameters exist
if [ -n "$LTE_PARAMS" ]; then
execute_at_command "AT+QNWLOCK=\"common/4g\",$LTE_PARAMS"
fi
# Enable NR5G lock if parameters exist
if [ -n "$NR5G_PARAMS" ]; then
execute_at_command "AT+QNWLOCK=\"common/5g\",$NR5G_PARAMS"
fi
;;
disable)
# Disable LTE lock
execute_at_command "AT+QNWLOCK=\"common/4g\",0"
# Disable NR5G lock
execute_at_command "AT+QNWLOCK=\"common/5g\",0"
;;
*)
log_message "ERROR" "Invalid action: $ACTION"
exit 1
;;
esac
# Restart network registration to apply changes
execute_at_command "AT+COPS=2"
sleep 2
execute_at_command "AT+COPS=0"
exit 0
EOL
chmod +x "$CELL_LOCK_SCRIPT"
log_message "INFO" "Created cell lock script at $CELL_LOCK_SCRIPT"
fi
}
# Function to remove set_cell_lock.sh script
remove_cell_lock_script() {
if [ -f "$CELL_LOCK_SCRIPT" ]; then
rm "$CELL_LOCK_SCRIPT"
log_message "INFO" "Removed cell lock script"
fi
}
# Function to urldecode
urldecode() {
echo -e "$(echo "$1" | sed 's/+/ /g;s/%\([0-9A-F][0-9A-F]\)/\\x\1/g')"
}
# Function to convert HH:MM to cron format
convert_to_cron_time() {
echo "$1" | awk -F: '{print $2, $1}'
}
# Function to save configuration
save_config() {
echo "START_TIME=$1" >"$CONFIG_FILE"
echo "END_TIME=$2" >>"$CONFIG_FILE"
echo "ENABLED=1" >>"$CONFIG_FILE"
log_message "INFO" "Saved configuration - Start: $1, End: $2"
}
# Function to disable scheduling
disable_scheduling() {
if [ -f "$CONFIG_FILE" ]; then
sed -i 's/ENABLED=1/ENABLED=0/' "$CONFIG_FILE"
log_message "INFO" "Disabled scheduling"
fi
crontab -l | grep -v "set_cell_lock.sh" | crontab -
remove_cell_lock_script
}
# Function to get current status
get_status() {
if [ -f "$CONFIG_FILE" ]; then
ENABLED=$(grep "ENABLED=" "$CONFIG_FILE" | cut -d'=' -f2)
START_TIME=$(grep "START_TIME=" "$CONFIG_FILE" | cut -d'=' -f2)
END_TIME=$(grep "END_TIME=" "$CONFIG_FILE" | cut -d'=' -f2)
echo "Status: 200 OK"
echo "Content-Type: application/json"
echo ""
echo "{\"enabled\":$ENABLED,\"start_time\":\"$START_TIME\",\"end_time\":\"$END_TIME\"}"
else
echo "Status: 200 OK"
echo "Content-Type: application/json"
echo ""
echo "{\"enabled\":0,\"start_time\":\"\",\"end_time\":\"\"}"
fi
}
# Handle POST requests
if [ "$REQUEST_METHOD" = "POST" ]; then
read -r POST_DATA
if echo "$POST_DATA" | grep -q "disable=true"; then
disable_scheduling
echo "Status: 200 OK"
echo "Content-Type: application/json"
echo ""
echo "{\"status\":\"success\",\"message\":\"Scheduling disabled\"}"
exit 0
fi
START_TIME=$(echo "$POST_DATA" | grep -o 'start_time=[^&]*' | cut -d'=' -f2)
END_TIME=$(echo "$POST_DATA" | grep -o 'end_time=[^&]*' | cut -d'=' -f2)
START_TIME=$(urldecode "$START_TIME")
END_TIME=$(urldecode "$END_TIME")
if [ -z "$START_TIME" ] || [ -z "$END_TIME" ]; then
log_message "ERROR" "Missing start or end time"
echo "Status: 400 Bad Request"
echo "Content-Type: application/json"
echo ""
echo "{\"error\":\"Missing start or end time\"}"
exit 1
fi
create_cell_lock_script
CRON_START=$(convert_to_cron_time "$START_TIME")
CRON_END=$(convert_to_cron_time "$END_TIME")
save_config "$START_TIME" "$END_TIME"
# Check current cell lock status and get parameters
LTE_STATUS=$(execute_at_command 'AT+QNWLOCK="common/4g"')
NR5G_STATUS=$(execute_at_command 'AT+QNWLOCK="common/5g"')
LTE_PARAMS=$(echo "$LTE_STATUS" | grep -o '"common/4g",[^[:space:]]*' | cut -d',' -f2-)
NR5G_PARAMS=$(echo "$NR5G_STATUS" | grep -o '"common/5g",[^[:space:]]*' | cut -d',' -f2-)
TEMP_CRON=$(mktemp)
crontab -l 2>/dev/null | grep -v "set_cell_lock.sh" >"$TEMP_CRON"
echo "$CRON_START * * * $CELL_LOCK_SCRIPT enable \"$LTE_PARAMS\" \"$NR5G_PARAMS\"" >>"$TEMP_CRON"
echo "$CRON_END * * * $CELL_LOCK_SCRIPT disable" >>"$TEMP_CRON"
crontab "$TEMP_CRON"
rm "$TEMP_CRON"
log_message "INFO" "Scheduling enabled with start time $START_TIME and end time $END_TIME"
echo "Status: 200 OK"
echo "Content-Type: application/json"
echo ""
echo "{\"status\":\"success\",\"message\":\"Scheduling enabled\"}"
exit 0
fi
# Parse query string for GET requests
if [ "$REQUEST_METHOD" = "GET" ]; then
QUERY_STRING=$(echo "$QUERY_STRING" | sed 's/&/\n/g')
for param in $QUERY_STRING; do
case "$param" in
status=*)
get_status
exit 0
;;
esac
done
fi
# If no valid request is made
log_message "ERROR" "Invalid request received"
echo "Status: 400 Bad Request"
echo "Content-Type: application/json"
echo ""
echo "{\"error\":\"Invalid request\"}"
exit 1

View File

@@ -0,0 +1,50 @@
#!/bin/sh
# Set content type
printf "Content-Type: application/json\n\n"
# URL decode function
urldecode() {
echo "$*" | sed 's/+/ /g;s/%\([0-9A-F][0-9A-F]\)/\\\\x\1/g' | xargs -0 printf '%b'
}
# Extract indexes from query string
query=$(echo "$QUERY_STRING" | grep -o 'indexes=[^&]*' | cut -d= -f2)
indexes=$(urldecode "$query")
# Function to output JSON response
send_json() {
printf '{"status":"%s","message":"%s"}\n' "$1" "$2"
}
# Validate input
if [ -z "$indexes" ]; then
send_json "error" "No indexes provided"
exit 0
fi
# Initialize counters
success=0
failure=0
# Process each index
echo "$indexes" | tr ',' '\n' | while read -r index; do
if [ -n "$index" ] && [ "$index" -eq "$index" ] 2>/dev/null; then
if sms_tool delete "$index" 2>/dev/null; then
success=$((success + 1))
else
failure=$((failure + 1))
fi
fi
done
# Send response
if [ $success -gt 0 ]; then
if [ $failure -eq 0 ]; then
send_json "success" "Successfully deleted $success message(s)"
else
send_json "partial" "Deleted $success message(s), failed to delete $failure message(s)"
fi
else
send_json "error" "Failed to delete messages"
fi

View File

@@ -0,0 +1,10 @@
#!/bin/sh
printf "Content-type: application/json\r\n\r\n"
# Execute the command and return the JSON response
if command -v sms_tool > /dev/null 2>&1; then
sms_tool -j recv
else
printf '{"error": "sms_tool not found"}\n'
fi

View File

@@ -0,0 +1,57 @@
#!/bin/sh
echo "Content-Type: application/json"
echo "Cache-Control: no-cache"
echo ""
# Function to URL decode the string
urldecode() {
local url_encoded="${1//+/ }"
printf '%b' "${url_encoded//%/\\x}"
}
# Function to escape JSON string
escape_json() {
printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\n/\\n/g; s/\r/\\r/g; s/\t/\\t/g'
}
# Read POST data
read -r QUERY_STRING
# Extract phone and message from POST data
phone=$(echo "$QUERY_STRING" | grep -o 'phone=[^&]*' | cut -d= -f2)
message=$(echo "$QUERY_STRING" | grep -o 'message=[^&]*' | cut -d= -f2)
# URL decode the message
decoded_message=$(urldecode "$message")
# Validate inputs
if [ -z "$phone" ] || [ -z "$message" ]; then
echo '{"success":false,"error":"Phone number and message are required"}'
exit 0
fi
# Validate phone number (only numbers allowed)
if ! echo "$phone" | grep -q '^[0-9]\+$'; then
echo '{"success":false,"error":"Invalid phone number format"}'
exit 0
fi
# Try to send SMS and capture output
result=$(sms_tool send "$phone" "$decoded_message" 2>&1)
escaped_result=$(escape_json "$result")
# Check if SMS was sent successfully by looking for "sms sent sucessfully"
if echo "$result" | grep -q "sms sent sucessfully"; then
# Extract the message ID if present
message_id=$(echo "$result" | grep -o '[0-9]*$')
echo "{\"success\":true,\"message\":\"SMS sent successfully\",\"messageId\":\"$message_id\",\"raw\":\"$escaped_result\"}"
elif echo "$result" | grep -q "sms not sent, code 350"; then
# Kill any hanging sms_tool process
pkill -f "sms_tool send"
echo '{"success":false,"error":"No prepaid credit available"}'
else
# Kill any hanging sms_tool process
pkill -f "sms_tool send"
echo "{\"success\":false,\"error\":\"Failed to send SMS\",\"raw\":\"$escaped_result\"}"
fi

View File

@@ -0,0 +1,168 @@
#!/bin/sh
# Set content type to JSON
echo "Content-type: application/json"
echo ""
# Configuration
QUEUE_DIR="/tmp/at_queue"
RESULTS_DIR="$QUEUE_DIR/results"
TOKEN_FILE="$QUEUE_DIR/token"
RESULT_FILE="/tmp/qscan_result.json"
WORKER_SCRIPT="/www/cgi-bin/quecmanager/experimental/cell_scanner/cell_scan_worker.sh"
PID_FILE="/tmp/cell_scan.pid"
SCAN_COMMAND="AT+QSCAN=3,1"
SCAN_TIMEOUT=200
LOCK_ID="CELL_SCAN_$(date +%s)_$$"
# Function to log messages
log_message() {
local level="${2:-info}"
logger -t at_queue -p "daemon.$level" "cell_scan: $1"
}
# Function to output JSON response
output_json() {
local status="$1"
local message="$2"
log_message "Sending response: status=$status, message=$message"
printf '{"status":"%s","message":"%s"}\n' "$status" "$message"
exit 0
}
# Function to check if worker is running
check_worker_running() {
if [ -f "$PID_FILE" ]; then
pid=$(cat "$PID_FILE")
if kill -0 "$pid" 2>/dev/null; then
log_message "Worker process $pid is running"
return 0
fi
log_message "Removing stale PID file for process $pid"
rm -f "$PID_FILE"
fi
return 1
}
# Enhanced JSON string escaping function
escape_json() {
printf '%s' "$1" | awk '
BEGIN { RS="\n"; ORS="\\n" }
{
gsub(/\\/, "\\\\")
gsub(/"/, "\\\"")
gsub(/\r/, "")
gsub(/\t/, "\\t")
gsub(/\f/, "\\f")
gsub(/\b/, "\\b")
print
}
' | sed 's/\\n$//'
}
# Acquire token directly with high priority
acquire_token() {
local priority=1 # Highest priority for cell scan
local max_attempts=10
local attempt=0
while [ $attempt -lt $max_attempts ]; do
# Check if token file exists
if [ -f "$TOKEN_FILE" ]; then
local current_holder=$(cat "$TOKEN_FILE" | jsonfilter -e '@.id' 2>/dev/null)
local current_priority=$(cat "$TOKEN_FILE" | jsonfilter -e '@.priority' 2>/dev/null)
local timestamp=$(cat "$TOKEN_FILE" | jsonfilter -e '@.timestamp' 2>/dev/null)
local current_time=$(date +%s)
# Check for expired token (> 30 seconds old)
if [ $((current_time - timestamp)) -gt 30 ] || [ -z "$current_holder" ]; then
# Remove expired token
rm -f "$TOKEN_FILE" 2>/dev/null
elif [ $priority -lt $current_priority ]; then
# Preempt lower priority token
log_message "Preempting token from $current_holder (priority: $current_priority)" "info"
rm -f "$TOKEN_FILE" 2>/dev/null
else
# Try again - higher priority token exists
log_message "Token held by $current_holder with priority $current_priority, retrying..." "debug"
sleep 0.5
attempt=$((attempt + 1))
continue
fi
fi
# Try to create token file
echo "{\"id\":\"$LOCK_ID\",\"priority\":$priority,\"timestamp\":$(date +%s)}" > "$TOKEN_FILE" 2>/dev/null
chmod 644 "$TOKEN_FILE" 2>/dev/null
# Verify we got the token
local holder=$(cat "$TOKEN_FILE" 2>/dev/null | jsonfilter -e '@.id' 2>/dev/null)
if [ "$holder" = "$LOCK_ID" ]; then
log_message "Successfully acquired token with priority $priority" "info"
return 0
fi
sleep 0.5
attempt=$((attempt + 1))
done
log_message "Failed to acquire token after $max_attempts attempts" "error"
return 1
}
# Main execution
{
# If scan is running, return running status
if check_worker_running; then
output_json "running" "Cell scan is in progress"
fi
# Start new scan
rm -f "$RESULT_FILE"
log_message "Starting new cell scan" "info"
# Ensure worker script is executable
chmod +x "$WORKER_SCRIPT" 2>/dev/null
# Start worker script with proper parameters
log_message "Attempting to start worker script: $WORKER_SCRIPT" "info"
# Check if worker script exists
if [ ! -f "$WORKER_SCRIPT" ]; then
log_message "Worker script not found: $WORKER_SCRIPT" "error"
output_json "error" "Worker script not found"
fi
# Ensure QUEUE_DIR exists
mkdir -p "$QUEUE_DIR" "$RESULTS_DIR"
chmod 755 "$QUEUE_DIR"
chmod 755 "$RESULTS_DIR"
# Start worker with debug logging
WORKER_PID=$
(sh "$WORKER_SCRIPT" >/tmp/cell_scan_worker.log 2>&1) &
WORKER_PID=$!
log_message "Worker script started with PID $WORKER_PID" "info"
# The worker process runs in the background and completes quickly
# We don't need to check if it's still running as it might finish before we check
log_message "Worker process $WORKER_PID started in background" "info"
# Instead of checking if the process is running, check if it created the result file
sleep 2
if [ -f "$RESULT_FILE" ]; then
log_message "Worker successfully created result file" "info"
else
log_message "Waiting for worker to create result file..." "info"
# If no result file yet, check for errors
if [ -f "/tmp/cell_scan_worker.log" ]; then
WORKER_LOG=$(cat "/tmp/cell_scan_worker.log" | head -20)
log_message "Worker log: $WORKER_LOG" "info"
fi
fi
output_json "running" "Started new cell scan"
} || {
# Error handler
log_message "Script failed with error" "error"
output_json "error" "Internal error occurred"
}

View File

@@ -0,0 +1,323 @@
#!/bin/sh
# Configuration
QUEUE_DIR="/tmp/at_queue"
RESULTS_DIR="$QUEUE_DIR/results"
TOKEN_FILE="$QUEUE_DIR/token"
RESULT_FILE="/tmp/qscan_result.json"
PID_FILE="/tmp/cell_scan.pid"
SCAN_COMMAND="AT+QSCAN=3,1"
SCAN_TIMEOUT=200
LOCK_ID="CELL_SCAN_$(date +%s)_$$"
# Enable shell debugging for better logging
set -x
# Function to log messages
log_message() {
local level="${2:-info}"
logger -t at_queue -p "daemon.$level" "cell_scan_worker: $1"
}
# Function to clean up stale temporary files
cleanup_stale_files() {
log_message "Cleaning up stale temporary files" "info"
# Clean up old start_time files (older than 1 hour)
find "$QUEUE_DIR" -name "start_time.qscan_*" -type f -mmin +60 -delete 2>/dev/null
# Clean up any start_time files that match our current process just in case
find "$QUEUE_DIR" -name "start_time.qscan_*_$" -type f -delete 2>/dev/null
log_message "Stale file cleanup completed" "info"
}
# Function to check directories and permissions
check_environment() {
log_message "Checking environment" "info"
# Clean up stale files first
cleanup_stale_files
# Check if directories exist, create if they don't
if [ ! -d "$QUEUE_DIR" ]; then
mkdir -p "$QUEUE_DIR"
log_message "Created queue directory: $QUEUE_DIR" "info"
fi
if [ ! -d "$RESULTS_DIR" ]; then
mkdir -p "$RESULTS_DIR"
log_message "Created results directory: $RESULTS_DIR" "info"
fi
# Check permissions
chmod 755 "$QUEUE_DIR" 2>/dev/null
chmod 755 "$RESULTS_DIR" 2>/dev/null
# Check if sms_tool exists and is executable
if ! which sms_tool >/dev/null 2>&1; then
log_message "sms_tool not found in PATH" "error"
return 1
fi
# Test directory write permissions
if ! touch "$QUEUE_DIR/test_$$" 2>/dev/null; then
log_message "Cannot write to $QUEUE_DIR" "error"
return 1
fi
rm -f "$QUEUE_DIR/test_$$" 2>/dev/null
if ! touch "$RESULTS_DIR/test_$$" 2>/dev/null; then
log_message "Cannot write to $RESULTS_DIR" "error"
return 1
fi
rm -f "$RESULTS_DIR/test_$$" 2>/dev/null
log_message "Environment check passed" "info"
return 0
}
# Function to clean AT command output
clean_output() {
while IFS= read -r line; do
case "$line" in
"OK" | "" | *"ERROR"*)
continue
;;
*)
printf '%s\n' "$line"
;;
esac
done | sed 's/\r//g' | tr '\n' '\r' | sed 's/\r$//' | tr '\r' '\n'
}
# Enhanced JSON string escaping function
escape_json() {
printf '%s' "$1" | awk '
BEGIN { RS="\n"; ORS="\\n" }
{
gsub(/\\/, "\\\\")
gsub(/"/, "\\\"")
gsub(/\r/, "")
gsub(/\t/, "\\t")
gsub(/\f/, "\\f")
gsub(/\b/, "\\b")
print
}
' | sed 's/\\n$//'
}
# Function to check if scan is already running
check_running() {
if [ -f "$PID_FILE" ]; then
pid=$(cat "$PID_FILE")
if kill -0 "$pid" 2>/dev/null; then
log_message "Cell scan already running (PID: $pid)" "warn"
return 0
fi
rm -f "$PID_FILE"
fi
return 1
}
# Acquire token directly with high priority
acquire_token() {
local priority=1 # Highest priority for cell scan
local max_attempts=10
local attempt=0
while [ $attempt -lt $max_attempts ]; do
# Check if token file exists
if [ -f "$TOKEN_FILE" ]; then
local current_holder=$(cat "$TOKEN_FILE" | jsonfilter -e '@.id' 2>/dev/null)
local current_priority=$(cat "$TOKEN_FILE" | jsonfilter -e '@.priority' 2>/dev/null)
local timestamp=$(cat "$TOKEN_FILE" | jsonfilter -e '@.timestamp' 2>/dev/null)
local current_time=$(date +%s)
# Check for expired token (> 30 seconds old)
if [ $((current_time - timestamp)) -gt 30 ] || [ -z "$current_holder" ]; then
# Remove expired token
rm -f "$TOKEN_FILE" 2>/dev/null
elif [ $priority -lt $current_priority ]; then
# Preempt lower priority token
log_message "Preempting token from $current_holder (priority: $current_priority)" "info"
rm -f "$TOKEN_FILE" 2>/dev/null
else
# Try again
sleep 0.5
attempt=$((attempt + 1))
continue
fi
fi
# Try to create token file
echo "{\"id\":\"$LOCK_ID\",\"priority\":$priority,\"timestamp\":$(date +%s)}" > "$TOKEN_FILE" 2>/dev/null
chmod 644 "$TOKEN_FILE" 2>/dev/null
# Verify we got the token
local holder=$(cat "$TOKEN_FILE" 2>/dev/null | jsonfilter -e '@.id' 2>/dev/null)
if [ "$holder" = "$LOCK_ID" ]; then
log_message "Successfully acquired token with priority $priority" "info"
return 0
fi
sleep 0.5
attempt=$((attempt + 1))
done
log_message "Failed to acquire token after $max_attempts attempts" "error"
return 1
}
# Release token directly
release_token() {
if [ -f "$TOKEN_FILE" ]; then
local current_holder=$(cat "$TOKEN_FILE" | jsonfilter -e '@.id' 2>/dev/null)
if [ "$current_holder" = "$LOCK_ID" ]; then
rm -f "$TOKEN_FILE" 2>/dev/null
log_message "Released token" "info"
return 0
fi
log_message "Token held by $current_holder, not by us ($LOCK_ID)" "warn"
fi
return 1
}
# Main execution
main() {
# Start logging
log_message "Worker script started" "info"
# Check if already running
if check_running; then
log_message "Cell scan already running, exiting" "warn"
exit 1
fi
# Create PID file
echo "$$" > "$PID_FILE"
chmod 644 "$PID_FILE" 2>/dev/null
log_message "Created PID file: $$" "info"
# Set up cleanup on exit
trap 'log_message "Cleaning up and exiting" "info"; release_token; rm -f "$PID_FILE"; exit' INT TERM EXIT
# Acquire token for AT command execution
if ! acquire_token; then
log_message "Failed to acquire token, exiting" "error"
rm -f "$PID_FILE"
exit 1
fi
log_message "Token acquired, executing scan command: $SCAN_COMMAND" "info"
# Execute scan with native timeout option (without relying on timeout command)
# Use the -t option of sms_tool instead of the timeout command
log_message "Executing command with timeout: $SCAN_TIMEOUT seconds" "info"
SCAN_OUTPUT=$(sms_tool at "$SCAN_COMMAND" -t $SCAN_TIMEOUT 2>&1 | clean_output)
SCAN_STATUS=$?
log_message "Command execution completed with status: $SCAN_STATUS" "info"
# Process and store result
if [ $SCAN_STATUS -eq 0 ]; then
# Check if output contains valid scan data or error
if echo "$SCAN_OUTPUT" | grep -q "+QSCAN"; then
# Set timestamp
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
# Valid scan data found - don't add the "Scan completed but no valid data" prefix
log_message "Scan completed with valid data" "info"
# Create the result file with proper JSON formatting
printf '{"status":"success","timestamp":"%s","output":%s}\n' \
"$TIMESTAMP" \
"$(printf '%s' "$SCAN_OUTPUT" | sed 's/"/\\"/g' | jq -R -s '.')" > "$RESULT_FILE"
chmod 644 "$RESULT_FILE" 2>/dev/null
else
# No valid scan data, but command completed
log_message "Command completed but no valid scan data found: $SCAN_OUTPUT" "warn"
SCAN_OUTPUT="Scan completed but no valid data returned: $SCAN_OUTPUT"
# Create a result file indicating partial success
printf '{"status":"partial","timestamp":"%s","output":%s}\n' \
"$(date '+%Y-%m-%d %H:%M:%S')" \
"$(printf '%s' "$SCAN_OUTPUT" | sed 's/"/\\"/g' | jq -R -s '.')" > "$RESULT_FILE"
chmod 644 "$RESULT_FILE" 2>/dev/null
fi
# Generate a command ID for the AT queue results format - use actual PID instead of $
local my_pid="$$"
local cmd_id="qscan_$(date +%s)_${my_pid}"
local end_time=$(date +%s)
local start_time=$end_time
local duration=0
# Store start time for future reference
echo "$start_time" > "$QUEUE_DIR/start_time.$cmd_id"
log_message "Creating AT queue result with ID: $cmd_id" "info"
# Create JSON response in the AT queue format
local response=$(cat << EOF
{
"command": {
"id": "$cmd_id",
"text": "$SCAN_COMMAND",
"timestamp": "$(date -Iseconds)"
},
"response": {
"status": "success",
"raw_output": "$(escape_json "$SCAN_OUTPUT")",
"completion_time": "$end_time",
"duration_ms": $duration
}
}
EOF
)
# Save the response to the AT queue results directory
printf "%s" "$response" > "$RESULTS_DIR/$cmd_id.json"
chmod 644 "$RESULTS_DIR/$cmd_id.json"
# Clean up temporary files
rm -f "$QUEUE_DIR/start_time.$cmd_id"
log_message "Cleaned up temporary files" "info"
# Release the token
release_token
return 0
else
log_message "Scan failed with status: $SCAN_STATUS" "error"
printf '{"status":"error","timestamp":"%s","message":"Scan failed"}\n' \
"$(date '+%Y-%m-%d %H:%M:%S')" > "$RESULT_FILE"
chmod 644 "$RESULT_FILE" 2>/dev/null
# Release the token
release_token
return 1
fi
}
# Execute main function with proper error handling
{
log_message "Worker script started with PID $$" "info"
# Check environment before proceeding
check_environment || {
log_message "Environment check failed, aborting" "error"
exit 1
}
# Execute main function
main || {
log_message "Main function failed with error $?" "error"
release_token
rm -f "$PID_FILE"
exit 1
}
} 2>/tmp/cell_scan_worker_debug.log || {
log_message "Script failed with error" "error"
release_token
rm -f "$PID_FILE"
exit 1
}

View File

@@ -0,0 +1,104 @@
#!/bin/sh
# Set content type to JSON
echo "Content-type: application/json"
echo ""
# Configuration
QUEUE_DIR="/tmp/at_queue"
RESULTS_DIR="$QUEUE_DIR/results"
RESULT_FILE="/tmp/qscan_result.json"
PID_FILE="/tmp/cell_scan.pid"
TOKEN_FILE="$QUEUE_DIR/token"
# Function to log messages
log_message() {
local level="${2:-info}"
logger -t at_queue -p "daemon.$level" "check_scan: $1"
}
# Function to output JSON response
output_json() {
local status="$1"
local message="$2"
if [ "$status" = "success" ] && [ -f "$RESULT_FILE" ]; then
# Return the contents of the result file
cat "$RESULT_FILE"
else
printf '{"status":"%s","message":"%s","timestamp":"","output":""}\n' "$status" "$message"
fi
}
# Check for scan token holder
check_token_holder() {
if [ -f "$TOKEN_FILE" ]; then
local current_holder=$(cat "$TOKEN_FILE" | jsonfilter -e '@.id' 2>/dev/null)
if [ -n "$current_holder" ] && echo "$current_holder" | grep -q "CELL_SCAN"; then
log_message "Cell scan token is active: $current_holder" "debug"
return 0
fi
fi
return 1
}
# Check if a scan is already in progress
check_scan_progress() {
# First check PID file
if [ -f "$PID_FILE" ]; then
pid=$(cat "$PID_FILE")
if kill -0 "$pid" 2>/dev/null; then
log_message "Scan in progress (PID: $pid)" "info"
output_json "running" "Scan in progress"
exit 0
else
log_message "Removing stale PID file" "warn"
rm -f "$PID_FILE"
fi
fi
# Also check token holder
if check_token_holder; then
log_message "Scan in progress (Token active)" "info"
output_json "running" "Scan in progress (Token active)"
exit 0
fi
}
# Check for existing results
check_results() {
if [ -f "$RESULT_FILE" ]; then
# Check if the result file contains valid JSON data
local result_content=$(cat "$RESULT_FILE" 2>/dev/null)
if [ -n "$result_content" ] && echo "$result_content" | grep -q "status"; then
# REMOVED AGE CHECK - Always return the file contents regardless of age
log_message "Found valid result file, returning contents" "info"
output_json "success" "Scan results available"
exit 0
else
log_message "Result file exists but contains invalid data" "warn"
rm -f "$RESULT_FILE" # Remove invalid result file
output_json "idle" "Invalid previous scan results"
exit 0
fi
fi
}
# Main execution
{
# First check if a scan is in progress
check_scan_progress
# Then check for existing results
check_results
# If no results and no running scan, indicate idle state
log_message "No active scan or recent results" "info"
output_json "idle" "No active scan"
exit 0
} || {
# Error handler
log_message "Failed to check scan status" "error"
output_json "error" "Failed to check scan status"
exit 1
}

View File

@@ -0,0 +1,40 @@
#!/bin/sh
# Set content type to JSON
echo "Content-type: application/json"
echo ""
# Configuration
JSON_FILE="/www/cgi-bin/quecmanager/mcc-mnc-list.json"
# Function to log messages
log_message() {
logger -t fetch_mccmnc "$1"
}
# Function to output JSON response
output_json() {
local status="$1"
local message="$2"
printf '{"status":"%s","message":"%s"}\n' "$status" "$message"
exit 1
}
# Main execution
{
# Check if file exists
if [ ! -f "$JSON_FILE" ]; then
log_message "MCC-MNC list file not found"
output_json "error" "MCC-MNC list file not found"
fi
# Read and output the file
cat "$JSON_FILE" 2>/dev/null || {
log_message "Failed to read MCC-MNC list file"
output_json "error" "Failed to read MCC-MNC list file"
}
} || {
# Error handler
log_message "Script failed with error"
output_json "error" "Internal error occurred"
}

View File

@@ -0,0 +1,311 @@
#!/bin/sh
# Set content-type for JSON response
echo "Content-type: application/json"
echo ""
# Define paths and constants to match queue system
QUEUE_DIR="/tmp/at_queue"
RESULTS_DIR="$QUEUE_DIR/results"
TOKEN_FILE="$QUEUE_DIR/token"
TEMP_FILE="/tmp/network_info_output.txt"
LOCK_ID="NETWORK_INFO_$(date +%s)_$$"
COMMAND_TIMEOUT=8 # Increased timeout
MAX_TOKEN_WAIT=10
PRIORITY=5 # Medium-high priority (between cell scan and normal commands)
# Function to log messages
log_message() {
local level="${2:-info}"
logger -t at_queue -p "daemon.$level" "network_info: $1"
}
# Function to output JSON error
output_error() {
printf '{"status":"error","message":"%s","timestamp":"%s"}\n' "$1" "$(date '+%H:%M:%S')"
exit 1
}
# Enhanced JSON string escaping function
escape_json() {
printf '%s' "$1" | awk '
BEGIN { RS="\n"; ORS="\\n" }
{
gsub(/\\/, "\\\\")
gsub(/"/, "\\\"")
gsub(/\r/, "")
gsub(/\t/, "\\t")
gsub(/\f/, "\\f")
gsub(/\b/, "\\b")
print
}
' | sed 's/\\n$//'
}
# Acquire token directly with medium-high priority
acquire_token() {
local priority="$PRIORITY" # Medium-high priority for network info
local max_attempts=$MAX_TOKEN_WAIT
local attempt=0
while [ $attempt -lt $max_attempts ]; do
# Check if token file exists
if [ -f "$TOKEN_FILE" ]; then
local current_holder=$(cat "$TOKEN_FILE" | jsonfilter -e '@.id' 2>/dev/null)
local current_priority=$(cat "$TOKEN_FILE" | jsonfilter -e '@.priority' 2>/dev/null)
local timestamp=$(cat "$TOKEN_FILE" | jsonfilter -e '@.timestamp' 2>/dev/null)
local current_time=$(date +%s)
# Check for expired token (> 30 seconds old)
if [ $((current_time - timestamp)) -gt 30 ] || [ -z "$current_holder" ]; then
# Remove expired token
rm -f "$TOKEN_FILE" 2>/dev/null
elif [ $priority -lt $current_priority ]; then
# Preempt lower priority token
log_message "Preempting token from $current_holder (priority: $current_priority)" "info"
rm -f "$TOKEN_FILE" 2>/dev/null
else
# Try again - higher priority token exists
log_message "Token held by $current_holder with priority $current_priority, retrying..." "debug"
sleep 0.5
attempt=$((attempt + 1))
continue
fi
fi
# Try to create token file
echo "{\"id\":\"$LOCK_ID\",\"priority\":$priority,\"timestamp\":$(date +%s)}" > "$TOKEN_FILE" 2>/dev/null
chmod 644 "$TOKEN_FILE" 2>/dev/null
# Verify we got the token
local holder=$(cat "$TOKEN_FILE" 2>/dev/null | jsonfilter -e '@.id' 2>/dev/null)
if [ "$holder" = "$LOCK_ID" ]; then
log_message "Successfully acquired token with priority $priority" "info"
return 0
fi
sleep 0.5
attempt=$((attempt + 1))
done
log_message "Failed to acquire token after $max_attempts attempts" "error"
return 1
}
# Release token directly
release_token() {
if [ -f "$TOKEN_FILE" ]; then
local current_holder=$(cat "$TOKEN_FILE" | jsonfilter -e '@.id' 2>/dev/null)
if [ "$current_holder" = "$LOCK_ID" ]; then
rm -f "$TOKEN_FILE" 2>/dev/null
log_message "Released token" "info"
return 0
fi
log_message "Token held by $current_holder, not by us ($LOCK_ID)" "warn"
fi
return 1
}
# Function to execute AT command with direct output capture
execute_at_command() {
local CMD="$1"
local OUTPUT_FILE="$TEMP_FILE.cmd.$$"
log_message "Executing command: $CMD" "debug"
# Execute command and redirect output to file for reliable capture
sms_tool at "$CMD" -t $COMMAND_TIMEOUT > "$OUTPUT_FILE" 2>&1
local EXIT_CODE=$?
# Read the output regardless of exit code
if [ -f "$OUTPUT_FILE" ]; then
local OUTPUT=$(cat "$OUTPUT_FILE")
rm -f "$OUTPUT_FILE"
if [ -n "$OUTPUT" ]; then
# We have some output
if echo "$OUTPUT" | grep -q "CME ERROR"; then
log_message "Command returned CME ERROR: $OUTPUT" "warn"
return 1
elif echo "$OUTPUT" | grep -q "ERROR"; then
log_message "Command returned ERROR: $OUTPUT" "warn"
return 1
else
# Command produced output that doesn't contain ERROR
log_message "Command executed successfully with output" "debug"
echo "$OUTPUT"
return 0
fi
elif [ $EXIT_CODE -eq 0 ]; then
log_message "Command succeeded but returned empty output" "warn"
echo "Command returned empty output"
return 0
else
log_message "Command failed with exit code $EXIT_CODE and no output" "error"
return 1
fi
else
log_message "Failed to create output file" "error"
return 1
fi
}
# Function to check network mode from serving cell info
check_network_mode() {
local OUTPUT="$1"
# Check for both LTE and NR5G-NSA (NSA mode)
if echo "$OUTPUT" | grep -q "\"LTE\"" && echo "$OUTPUT" | grep -q "\"NR5G-NSA\""; then
log_message "Detected network mode: NRLTE (NSA)" "info"
echo "NRLTE"
# Check for LTE only
elif echo "$OUTPUT" | grep -q "\"LTE\""; then
log_message "Detected network mode: LTE" "info"
echo "LTE"
# Check for NR5G-SA
elif echo "$OUTPUT" | grep -q "\"NR5G-SA\""; then
log_message "Detected network mode: NR5G (SA)" "info"
echo "NR5G"
else
log_message "Detected network mode: UNKNOWN from output: $OUTPUT" "warn"
echo "UNKNOWN"
fi
}
# Function to check NR5G measurement info setting
check_nr5g_meas_info() {
local OUTPUT=$(execute_at_command "AT+QNWCFG=\"nr5g_meas_info\"")
local EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ] && echo "$OUTPUT" | grep -q "\"nr5g_meas_info\",1"; then
log_message "NR5G measurement info is enabled" "debug"
return 0
else
log_message "NR5G measurement info is disabled or check failed" "debug"
return 1
fi
}
# Function to create JSON output safely
format_output_json() {
local MODE="$1"
local SERVING_OUTPUT="$2"
local NEIGHBOR_OUTPUT="$3"
local MEAS_OUTPUT="$4"
# Basic JSON structure - start
printf '{"status":"success","timestamp":"%s","mode":"%s"' "$(date '+%H:%M:%S')" "$MODE"
# Add raw data section
printf ',"raw_data":{'
# Add serving cell output (always present)
printf '"servingCell":%s' "$(printf '%s' "$SERVING_OUTPUT" | jq -R -s '.')"
# Add neighbor cells output if available
if [ -n "$NEIGHBOR_OUTPUT" ]; then
printf ',"neighborCells":%s' "$(printf '%s' "$NEIGHBOR_OUTPUT" | jq -R -s '.')"
fi
# Add measurement info output if available
if [ -n "$MEAS_OUTPUT" ]; then
printf ',"meas":%s' "$(printf '%s' "$MEAS_OUTPUT" | jq -R -s '.')"
fi
# Close raw data section
printf '}'
# Close the whole JSON object
printf '}\n'
}
# Set up trap for cleanup
trap 'log_message "Script interrupted, cleaning up" "warn"; release_token; rm -f "$TEMP_FILE" "$TEMP_FILE.cmd."*; exit 1' INT TERM EXIT
# Main execution
{
# Ensure directories exist
mkdir -p "$QUEUE_DIR" "$RESULTS_DIR"
log_message "Starting network info collection" "info"
# Acquire token for AT command execution before any output
if ! acquire_token; then
output_error "Failed to acquire token for command processing"
fi
# Get the serving cell information first
log_message "Getting serving cell information" "info"
SERVING_OUTPUT=$(execute_at_command "AT+QENG=\"servingcell\"")
EXIT_CODE=$?
# Check if we got valid serving cell info
if [ $EXIT_CODE -ne 0 ] || [ -z "$SERVING_OUTPUT" ]; then
log_message "Failed to get serving cell information, output: $SERVING_OUTPUT" "error"
release_token
output_error "Failed to get serving cell information"
fi
log_message "Successfully got serving cell information" "info"
# Determine network mode from serving cell output
NETWORK_MODE=$(check_network_mode "$SERVING_OUTPUT")
NEIGHBOR_OUTPUT=""
MEAS_OUTPUT=""
case "$NETWORK_MODE" in
"NRLTE")
log_message "Processing NRLTE mode commands" "info"
NEIGHBOR_OUTPUT=$(execute_at_command "AT+QENG=\"neighbourcell\"")
# Try to get measurement info
if ! check_nr5g_meas_info; then
log_message "Enabling NR5G measurement info" "info"
execute_at_command "AT+QNWCFG=\"nr5g_meas_info\",1" > /dev/null
sleep 1 # Give it time to take effect
fi
log_message "Fetching NR5G measurement info" "info"
MEAS_OUTPUT=$(execute_at_command "AT+QNWCFG=\"nr5g_meas_info\"")
;;
"LTE")
log_message "Processing LTE mode commands" "info"
NEIGHBOR_OUTPUT=$(execute_at_command "AT+QENG=\"neighbourcell\"")
;;
"NR5G")
log_message "Processing NR5G mode commands" "info"
# Try to get measurement info
if ! check_nr5g_meas_info; then
log_message "Enabling NR5G measurement info" "info"
execute_at_command "AT+QNWCFG=\"nr5g_meas_info\",1" > /dev/null
sleep 1 # Give it time to take effect
fi
log_message "Fetching NR5G measurement info" "info"
MEAS_OUTPUT=$(execute_at_command "AT+QNWCFG=\"nr5g_meas_info\"")
;;
*)
# Even if we don't recognize the mode, we'll still return the serving cell info
log_message "Unknown network mode, only returning serving cell info" "warn"
;;
esac
# Format and output JSON response
log_message "Formatting JSON response" "info"
format_output_json "$NETWORK_MODE" "$SERVING_OUTPUT" "$NEIGHBOR_OUTPUT" "$MEAS_OUTPUT"
# Release token and clean up
release_token
rm -f "$TEMP_FILE" "$TEMP_FILE.cmd."*
log_message "Network info collection completed" "info"
} || {
# Error handler
log_message "Script failed with error" "error"
release_token
rm -f "$TEMP_FILE" "$TEMP_FILE.cmd."*
output_error "Internal error occurred"
}

View File

@@ -0,0 +1,195 @@
#!/bin/sh
# Configuration
CONFIG_FILE="/etc/keep_alive_schedule.conf"
STATUS_FILE="/tmp/keep_alive_status"
SPEEDTEST_SCRIPT="/www/cgi-bin/home/speedtest/speedtest.sh"
# Function to convert HH:MM to minutes since midnight
time_to_minutes() {
echo "$1" | awk -F: '{print $1 * 60 + $2}'
}
# Function to validate time interval
validate_interval() {
START_TIME=$1
END_TIME=$2
INTERVAL_MINUTES=$3
# Convert times to minutes
START_MINUTES=$(time_to_minutes "$START_TIME")
END_MINUTES=$(time_to_minutes "$END_TIME")
# Calculate duration between start and end time
if [ $END_MINUTES -lt $START_MINUTES ]; then
# Handle case where end time is on the next day
DURATION=$((1440 - START_MINUTES + END_MINUTES))
else
DURATION=$((END_MINUTES - START_MINUTES))
fi
# Check if interval is longer than duration
if [ $INTERVAL_MINUTES -gt $DURATION ]; then
return 1
fi
return 0
}
# Function to generate cron time expression
generate_cron_time() {
START_TIME=$1
END_TIME=$2
INTERVAL=$3
START_HOUR=$(echo "$START_TIME" | cut -d: -f1 | sed 's/^0//')
START_MIN=$(echo "$START_TIME" | cut -d: -f2)
END_HOUR=$(echo "$END_TIME" | cut -d: -f1 | sed 's/^0//')
END_MIN=$(echo "$END_TIME" | cut -d: -f2)
# If end time is less than start time, it means we cross midnight
if [ $(time_to_minutes "$END_TIME") -lt $(time_to_minutes "$START_TIME") ]; then
# Create two cron entries for before and after midnight
echo "*/$INTERVAL $START_HOUR-23 * * * $SPEEDTEST_SCRIPT"
echo "*/$INTERVAL 0-$((END_HOUR - 1)) * * * $SPEEDTEST_SCRIPT"
else
echo "*/$INTERVAL $START_HOUR-$((END_HOUR - 1)) * * * $SPEEDTEST_SCRIPT"
fi
}
# Function to urldecode
urldecode() {
echo -e "$(echo "$1" | sed 's/+/ /g;s/%\([0-9A-F][0-9A-F]\)/\\x\1/g')"
}
# Function to save configuration
save_config() {
echo "START_TIME=$1" >"$CONFIG_FILE"
echo "END_TIME=$2" >>"$CONFIG_FILE"
echo "INTERVAL=$3" >>"$CONFIG_FILE"
echo "ENABLED=1" >>"$CONFIG_FILE"
}
# Function to disable scheduling
disable_scheduling() {
if [ -f "$CONFIG_FILE" ]; then
sed -i 's/ENABLED=1/ENABLED=0/' "$CONFIG_FILE"
fi
# Remove any existing cron jobs
crontab -l | grep -v "$SPEEDTEST_SCRIPT" | crontab -
}
# Function to get current status
get_status() {
if [ -f "$CONFIG_FILE" ]; then
ENABLED=$(grep "ENABLED=" "$CONFIG_FILE" | cut -d'=' -f2)
START_TIME=$(grep "START_TIME=" "$CONFIG_FILE" | cut -d'=' -f2)
END_TIME=$(grep "END_TIME=" "$CONFIG_FILE" | cut -d'=' -f2)
INTERVAL=$(grep "INTERVAL=" "$CONFIG_FILE" | cut -d'=' -f2)
echo "Status: 200 OK"
echo "Content-Type: application/json"
echo ""
echo "{\"enabled\":$ENABLED,\"start_time\":\"$START_TIME\",\"end_time\":\"$END_TIME\",\"interval\":$INTERVAL}"
else
echo "Status: 200 OK"
echo "Content-Type: application/json"
echo ""
echo "{\"enabled\":0,\"start_time\":\"\",\"end_time\":\"\",\"interval\":0}"
fi
}
# Handle POST requests
if [ "$REQUEST_METHOD" = "POST" ]; then
# Read POST data
read -r POST_DATA
# Check if disabling is requested
echo "$POST_DATA" | grep -q "disable=true"
if [ $? -eq 0 ]; then
disable_scheduling
echo "Status: 200 OK"
echo "Content-Type: application/json"
echo ""
echo "{\"status\":\"success\",\"message\":\"Scheduling disabled\"}"
exit 0
fi
# Extract times and interval
START_TIME=$(echo "$POST_DATA" | grep -o 'start_time=[^&]*' | cut -d'=' -f2)
END_TIME=$(echo "$POST_DATA" | grep -o 'end_time=[^&]*' | cut -d'=' -f2)
INTERVAL=$(echo "$POST_DATA" | grep -o 'interval=[^&]*' | cut -d'=' -f2)
# Decode times
START_TIME=$(urldecode "$START_TIME")
END_TIME=$(urldecode "$END_TIME")
INTERVAL=$(urldecode "$INTERVAL")
# Validate times
if [ -z "$START_TIME" ] || [ -z "$END_TIME" ] || [ -z "$INTERVAL" ]; then
echo "Status: 400 Bad Request"
echo "Content-Type: application/json"
echo ""
echo "{\"error\":\"Missing start time, end time, or interval\"}"
exit 1
fi
# Validate interval is a number
if ! echo "$INTERVAL" | grep -q '^[0-9]\+$'; then
echo "Status: 400 Bad Request"
echo "Content-Type: application/json"
echo ""
echo "{\"error\":\"Interval must be a number in minutes\"}"
exit 1
fi
# Validate interval
if ! validate_interval "$START_TIME" "$END_TIME" "$INTERVAL"; then
echo "Status: 400 Bad Request"
echo "Content-Type: application/json"
echo ""
echo "{\"error\":\"Interval is longer than the time between start and end time\"}"
exit 1
fi
# Create temporary file for new crontab
TEMP_CRON=$(mktemp)
# Get existing crontab entries (excluding our script)
crontab -l 2>/dev/null | grep -v "$SPEEDTEST_SCRIPT" >"$TEMP_CRON"
# Generate and add cron entries
generate_cron_time "$START_TIME" "$END_TIME" "$INTERVAL" >>"$TEMP_CRON"
# Install new crontab
crontab "$TEMP_CRON"
rm "$TEMP_CRON"
# Save configuration
save_config "$START_TIME" "$END_TIME" "$INTERVAL"
echo "Status: 200 OK"
echo "Content-Type: application/json"
echo ""
echo "{\"status\":\"success\",\"message\":\"Keep-alive scheduling enabled\"}"
exit 0
fi
# Parse query string for GET requests
if [ "$REQUEST_METHOD" = "GET" ]; then
QUERY_STRING=$(echo "$QUERY_STRING" | sed 's/&/\n/g')
for param in $QUERY_STRING; do
case "$param" in
status=*)
get_status
exit 0
;;
esac
done
fi
# If no valid request is made
echo "Status: 400 Bad Request"
echo "Content-Type: application/json"
echo ""
echo "{\"error\":\"Invalid request\"}"
exit 1

View File

@@ -0,0 +1,63 @@
#!/bin/sh
# Set headers for JSON response
echo "Content-type: application/json"
echo ""
# Disable the service in UCI
uci set quecmanager.quecwatch.enabled='0'
if ! uci commit quecmanager; then
echo '{"status":"error","message":"Failed to update configuration"}'
exit 1
fi
# Function to log cleanup events
log_message() {
local level="$1"
local message="$2"
local LOG_DIR="/tmp/log/quecwatch"
local LOG_FILE="${LOG_DIR}/quecwatch.log"
# Ensure log directory exists
mkdir -p "${LOG_DIR}"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "${timestamp} - [${level}] ${message}" >> "$LOG_FILE"
logger -t quecwatch "${level}: ${message}"
}
# Stop the service
if [ -x "/etc/init.d/quecwatch" ]; then
if ! /etc/init.d/quecwatch stop; then
log_message "ERROR" "Failed to stop service cleanly"
# Force kill any remaining processes
if pkill -f "/www/cgi-bin/services/quecwatch.sh"; then
log_message "INFO" "Forced termination of QuecWatch processes"
fi
else
log_message "INFO" "Service stopped successfully"
fi
# Disable the service
if ! /etc/init.d/quecwatch disable; then
log_message "WARN" "Failed to disable service"
else
log_message "INFO" "Service disabled successfully"
fi
fi
# Clean up temporary files
for file in "/tmp/quecwatch_status.json" "/tmp/quecwatch_retry_count" "/var/run/quecwatch.pid"; do
if [ -f "$file" ]; then
if rm -f "$file"; then
log_message "INFO" "Removed temporary file: $file"
else
log_message "WARN" "Failed to remove temporary file: $file"
fi
fi
done
# Return success
echo '{"status":"success","message":"QuecWatch disabled successfully"}'

View File

@@ -0,0 +1,137 @@
#!/bin/sh
# Set content type to JSON
echo "Content-type: application/json"
echo ""
# Read POST data
read -r POST_DATA
# Function to extract value from JSON post data
extract_json_value() {
local key="$1"
local default="$2"
# Try with jsonfilter
if command -v jsonfilter >/dev/null 2>&1; then
local value=$(echo "$POST_DATA" | jsonfilter -e "@.$key" 2>/dev/null)
[ -n "$value" ] && echo "$value" && return 0
fi
# Fallback to grep
local value=$(echo "$POST_DATA" | grep -o "\"$key\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" | cut -d'"' -f4)
[ -n "$value" ] && echo "$value" && return 0
# Fallback to grep for numbers and booleans
local value=$(echo "$POST_DATA" | grep -o "\"$key\"[[:space:]]*:[[:space:]]*[0-9a-zA-Z]*" | cut -d':' -f2 | tr -d '[:space:]')
[ -n "$value" ] && echo "$value" && return 0
# Return default value
echo "$default"
return 0
}
# Extract parameters from POST data
ping_target=$(extract_json_value "pingTarget" "8.8.8.8")
ping_interval=$(extract_json_value "pingInterval" "60")
ping_failures=$(extract_json_value "pingFailures" "3")
max_retries=$(extract_json_value "maxRetries" "5")
connection_refresh=$(extract_json_value "connectionRefresh" "false")
auto_sim_failover=$(extract_json_value "autoSimFailover" "false")
sim_failover_schedule=$(extract_json_value "simFailoverSchedule" "0")
# Validate numeric values
validate_number() {
local value="$1"
local min="$2"
local max="$3"
local name="$4"
if ! echo "$value" | grep -q '^[0-9]\+$'; then
echo '{"status":"error","message":"'"$name must be a number"'"}'
exit 1
fi
if [ "$value" -lt "$min" ] || [ "$value" -gt "$max" ]; then
echo '{"status":"error","message":"'"$name must be between $min and $max"'"}'
exit 1
fi
}
# Validate boolean values
validate_boolean() {
local value="$1"
local name="$2"
if [ "$value" != "true" ] && [ "$value" != "false" ]; then
echo '{"status":"error","message":"'"$name must be true or false"'"}'
exit 1
fi
}
# Validate parameters
validate_number "$ping_interval" 5 3600 "Ping interval"
validate_number "$ping_failures" 1 10 "Ping failures"
validate_number "$max_retries" 1 20 "Max retries"
validate_number "$sim_failover_schedule" 0 1440 "SIM failover schedule"
validate_boolean "$connection_refresh" "Connection refresh"
validate_boolean "$auto_sim_failover" "Auto SIM failover"
# Function to setup UCI configuration
setup_uci_config() {
# Create section if it doesn't exist
touch /etc/config/quecmanager
if ! uci -q get quecmanager.quecwatch >/dev/null; then
uci set quecmanager.quecwatch=service
fi
# Set UCI values
uci set quecmanager.quecwatch.enabled='1'
uci set quecmanager.quecwatch.ping_target="$ping_target"
uci set quecmanager.quecwatch.ping_interval="$ping_interval"
uci set quecmanager.quecwatch.ping_failures="$ping_failures"
uci set quecmanager.quecwatch.max_retries="$max_retries"
uci set quecmanager.quecwatch.current_retries='0'
uci set quecmanager.quecwatch.connection_refresh="$connection_refresh"
uci set quecmanager.quecwatch.refresh_count='3'
uci set quecmanager.quecwatch.auto_sim_failover="$auto_sim_failover"
uci set quecmanager.quecwatch.sim_failover_schedule="$sim_failover_schedule"
# Commit changes
if ! uci commit quecmanager; then
echo '{"status":"error","message":"Failed to save configuration"}'
exit 1
fi
return 0
}
# Setup configuration
if ! setup_uci_config; then
exit 1
fi
# Enable and start the service
if [ ! -f "/etc/init.d/quecwatch" ]; then
echo '{"status":"error","message":"QuecWatch service script not found"}'
exit 1
fi
# Make sure the service script is executable
chmod +x /etc/init.d/quecwatch
# Enable the service
if ! /etc/init.d/quecwatch enable; then
echo '{"status":"error","message":"Failed to enable QuecWatch service"}'
exit 1
fi
# Start the service
if ! /etc/init.d/quecwatch start; then
echo '{"status":"error","message":"Failed to start QuecWatch service"}'
exit 1
fi
# Return success response
echo '{"status":"success","message":"QuecWatch enabled successfully"}'

View File

@@ -0,0 +1,139 @@
#!/bin/sh
# Set headers for JSON response
echo "Content-type: application/json"
echo ""
# Load UCI functions
. /lib/functions.sh
# Function to safely get UCI value with default
get_uci_value() {
local value
config_get value quecwatch "$1" "$2"
echo "${value:-$2}"
}
# Function to format boolean for JSON
format_boolean() {
if [ "$1" = "1" ] || [ "$1" = "true" ]; then
echo "true"
else
echo "false"
fi
}
# Function to check if service is running
check_service_status() {
if [ -f "/var/run/quecwatch.pid" ]; then
pid=$(cat /var/run/quecwatch.pid 2>/dev/null)
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
echo "running"
return
fi
fi
echo "stopped"
}
# Function to get last log entry
get_last_log() {
local LOG_FILE="/tmp/log/quecwatch/quecwatch.log"
if [ -f "$LOG_FILE" ]; then
tail -n 1 "$LOG_FILE" | sed 's/"/\\"/g'
else
echo "No log entries found"
fi
}
# Function to get current status
get_current_status() {
local STATUS_FILE="/tmp/quecwatch_status.json"
local status="unknown"
local message="Status not available"
local retry="0"
local maxRetries="0"
local timestamp=$(date +%s)
if [ -f "$STATUS_FILE" ]; then
# Try to extract values from status file
if grep -q "status" "$STATUS_FILE"; then
status=$(cat "$STATUS_FILE" | jsonfilter -e '@.status' 2>/dev/null)
message=$(cat "$STATUS_FILE" | jsonfilter -e '@.message' 2>/dev/null)
retry=$(cat "$STATUS_FILE" | jsonfilter -e '@.retry' 2>/dev/null)
maxRetries=$(cat "$STATUS_FILE" | jsonfilter -e '@.maxRetries' 2>/dev/null)
timestamp=$(cat "$STATUS_FILE" | jsonfilter -e '@.timestamp' 2>/dev/null)
fi
fi
# Use defaults if extraction failed
[ -z "$status" ] && status="unknown"
[ -z "$message" ] && message="Status not available"
[ -z "$retry" ] && retry="0"
[ -z "$maxRetries" ] && maxRetries="0"
[ -z "$timestamp" ] && timestamp=$(date +%s)
echo "{\"status\":\"$status\",\"message\":\"$message\",\"retry\":$retry,\"maxRetries\":$maxRetries,\"timestamp\":$timestamp}"
}
# Load QuecManager configuration
config_load quecmanager
# Check if QuecWatch section exists
if ! uci -q get quecmanager.quecwatch >/dev/null; then
echo '{"status":"inactive","message":"QuecWatch is not configured"}'
exit 0
fi
# Get enabled status
enabled=$(get_uci_value "enabled" "0")
# Get service status
service_status=$(check_service_status)
# Get current status
current_status=$(get_current_status)
# Get last log entry
last_log=$(get_last_log)
# Fetch all configuration values
ping_target=$(get_uci_value "ping_target" "8.8.8.8")
ping_interval=$(get_uci_value "ping_interval" "60")
ping_failures=$(get_uci_value "ping_failures" "3")
max_retries=$(get_uci_value "max_retries" "5")
current_retries=$(get_uci_value "current_retries" "0")
connection_refresh=$(format_boolean $(get_uci_value "connection_refresh" "false"))
refresh_count=$(get_uci_value "refresh_count" "3")
auto_sim_failover=$(format_boolean $(get_uci_value "auto_sim_failover" "false"))
sim_failover_schedule=$(get_uci_value "sim_failover_schedule" "0")
# Determine the overall status
status="inactive"
if [ "$enabled" = "1" ]; then
if [ "$service_status" = "running" ]; then
status="active"
else
status="error"
fi
fi
# Prepare JSON response
cat <<EOF
{
"status": "$status",
"serviceStatus": "$service_status",
"currentStatus": $current_status,
"config": {
"pingTarget": "$ping_target",
"pingInterval": $ping_interval,
"pingFailures": $ping_failures,
"maxRetries": $max_retries,
"currentRetries": $current_retries,
"connectionRefresh": $connection_refresh,
"refreshCount": $refresh_count,
"autoSimFailover": $auto_sim_failover,
"simFailoverSchedule": $sim_failover_schedule
},
"lastActivity": "$last_log"
}
EOF

View File

@@ -0,0 +1,57 @@
#!/bin/sh
# Set headers for JSON response
echo "Content-type: application/json"
echo ""
# Function to log message
log_message() {
local level="$1"
local message="$2"
local LOG_DIR="/tmp/log/quecwatch"
local LOG_FILE="${LOG_DIR}/quecwatch.log"
# Ensure log directory exists
mkdir -p "${LOG_DIR}"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "${timestamp} - [${level}] ${message}" >> "$LOG_FILE"
logger -t quecwatch "${level}: ${message}"
}
# Reset retry counter
if uci -q get quecmanager.quecwatch >/dev/null; then
# Reset retry counter in UCI
uci set quecmanager.quecwatch.current_retries='0'
# Make sure service is enabled
uci set quecmanager.quecwatch.enabled='1'
# Commit changes
if uci commit quecmanager; then
log_message "INFO" "Retry counter reset to 0 and service enabled"
# Also update the retry count file for immediate effect
echo "0" > "/tmp/quecwatch_retry_count"
chmod 644 "/tmp/quecwatch_retry_count"
# Restart the service if it exists
if [ -x "/etc/init.d/quecwatch" ]; then
if /etc/init.d/quecwatch restart; then
log_message "INFO" "Service restarted successfully"
echo '{"status":"success","message":"Retry counter reset and service restarted successfully"}'
else
log_message "ERROR" "Failed to restart service"
echo '{"status":"warning","message":"Retry counter reset but failed to restart service"}'
fi
else
log_message "ERROR" "Service init script not found"
echo '{"status":"warning","message":"Retry counter reset but service init script not found"}'
fi
else
log_message "ERROR" "Failed to update configuration"
echo '{"status":"error","message":"Failed to update configuration"}'
fi
else
echo '{"status":"error","message":"QuecWatch configuration not found"}'
fi

View File

@@ -0,0 +1,10 @@
#!/bin/sh
echo "Content-Type: application/json"
echo "Cache-Control: no-cache, no-store, must-revalidate"
echo "Pragma: no-cache"
echo "Expires: 0"
echo ""
# Basic response indicating the server is up
echo '{"alive": true}'

View File

@@ -0,0 +1,14 @@
#!/bin/sh
# Set the content type to JSON
echo "Content-Type: application/json"
echo ""
# Ping 8.8.8.8 with 2 packets and capture the result
if ping -c 2 8.8.8.8 > /dev/null 2>&1; then
# Ping was successful
echo '{"connection": "ACTIVE"}'
else
# Ping failed
echo '{"connection": "INACTIVE"}'
fi

View File

@@ -0,0 +1,8 @@
#!/bin/sh
# Set content type to JSON
echo "Content-Type: application/json"
echo
# Read the JSON file and get only the last entry using jq
jq 'last' /www/signal_graphs/data_usage.json

View File

@@ -0,0 +1,119 @@
#!/bin/sh
# Set common headers
echo "Content-Type: application/json"
echo "Access-Control-Allow-Origin: *"
echo "Cache-Control: no-cache, no-store, must-revalidate"
echo ""
# Lock file path
LOCK_FILE="/tmp/hw_details.lock"
LOCK_TIMEOUT=10 # Maximum wait time in seconds
# Function to acquire lock
acquire_lock() {
local start_time=$(date +%s)
while [ -e "$LOCK_FILE" ]; do
# Check if lock is stale (older than LOCK_TIMEOUT seconds)
if [ -f "$LOCK_FILE" ]; then
local lock_time=$(stat -c %Y "$LOCK_FILE" 2>/dev/null)
local current_time=$(date +%s)
if [ $((current_time - lock_time)) -gt $LOCK_TIMEOUT ]; then
rm -f "$LOCK_FILE"
break
fi
fi
# Check if we've waited too long
if [ $(($(date +%s) - start_time)) -gt $LOCK_TIMEOUT ]; then
error_response "Timeout waiting for lock"
exit 1
fi
sleep 0.1
done
# Create lock file with current PID
echo $$ > "$LOCK_FILE"
}
# Function to release lock
release_lock() {
rm -f "$LOCK_FILE"
}
# Function to handle errors and return JSON
error_response() {
echo "{\"error\": \"$1\"}"
exit 1
}
# Function to cleanup on exit
cleanup() {
release_lock
exit $?
}
# Set trap for cleanup
trap cleanup EXIT INT TERM
# Function to get memory information
get_memory_info() {
free_output=$(free -b)
memory_info=$(echo "$free_output" | awk '/Mem:/ {print "{\"total\": " $2 ", \"used\": " $3 ", \"available\": " $7 "}"}')
echo "$memory_info"
}
# Function to get ethernet information
get_ethernet_info() {
interface=${1:-eth0}
# Check if ethtool is installed
if ! which ethtool >/dev/null 2>&1; then
error_response "ethtool not found"
fi
# Check if interface exists
if ! ip link show "$interface" >/dev/null 2>&1; then
error_response "Interface $interface not found"
fi
# Run ethtool and capture output
ethtool_output=$(ethtool "$interface" 2>/dev/null) || error_response "Failed to get ethernet information"
# Extract values using sed instead of grep -P
speed=$(echo "$ethtool_output" | sed -n 's/.*Speed: \([^[:space:]]*\).*/\1/p' || echo "Unknown")
link_status=$(echo "$ethtool_output" | sed -n 's/.*Link detected: \(yes\|no\).*/\1/p' || echo "unknown")
auto_negotiation=$(echo "$ethtool_output" | sed -n 's/.*Auto-negotiation: \(on\|off\).*/\1/p' || echo "unknown")
# Output JSON
echo "{\"link_speed\":\"$speed\",\"link_status\":\"$link_status\",\"auto_negotiation\":\"$auto_negotiation\"}"
}
# Main execution
# Acquire lock before proceeding
acquire_lock
# Parse query string for type and interface
type=$(echo "$QUERY_STRING" | sed -n 's/.*type=\([^&]*\).*/\1/p')
interface=$(echo "$QUERY_STRING" | sed -n 's/.*interface=\([^&]*\).*/\1/p')
# Default interface if not specified
[ -z "$interface" ] && interface="eth0"
# Convert type to lowercase using tr
type=$(echo "$type" | tr '[:upper:]' '[:lower:]')
# Check type parameter and call appropriate function
case "$type" in
"memory")
get_memory_info
;;
"eth")
get_ethernet_info "$interface"
;;
*)
error_response "Invalid type. Use 'memory' or 'eth'"
;;
esac
# Lock will be automatically released by the cleanup trap

View File

@@ -0,0 +1,30 @@
#!/bin/sh
# Ensure the script outputs proper CGI headers
echo "Content-Type: application/json"
echo ""
# Directory where JSON files are stored (adjust as needed)
JSON_DIR="/www/signal_graphs/"
# Function to safely read JSON file
read_json_file() {
local file="$1"
if [ -f "$file" ]; then
cat "$file"
else
echo "[]" # Return empty array if file doesn't exist
fi
}
# Collect signal metrics from JSON files
RSRP=$(read_json_file "${JSON_DIR}/rsrp.json")
RSRQ=$(read_json_file "${JSON_DIR}/rsrq.json")
SINR=$(read_json_file "${JSON_DIR}/sinr.json")
# Combine metrics into a single JSON object
printf '{
"rsrp": %s,
"rsrq": %s,
"sinr": %s
}' "$RSRP" "$RSRQ" "$SINR"

View File

@@ -0,0 +1,24 @@
#!/bin/sh
# Set the content type to JSON
echo "Content-Type: application/json"
echo ""
# Ping 8.8.8.8 with 5 packets and capture the full output
ping_result=$(ping -c 5 8.8.8.8)
# Check if ping was successful
if [ $? -eq 0 ]; then
# Extract the average latency using awk
avg_latency=$(echo "$ping_result" | awk '/avg/ {split($4, a, "/"); print int(a[2])}')
# If average latency was extracted, return it
if [ ! -z "$avg_latency" ]; then
echo "{\"connection\": \"ACTIVE\", \"latency\": $avg_latency}"
else
echo '{"connection": "ACTIVE", "latency": 0}'
fi
else
# Ping failed
echo '{"connection": "INACTIVE", "latency": 0}'
fi

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,95 @@
#!/bin/sh
# Location: /www/cgi-bin/quecmanager/profiles/check_status.cgi
# Set content type to JSON
echo "Content-type: application/json"
echo ""
# Configuration
STATUS_FILE="/tmp/quecprofiles_status.json"
TRACK_FILE="/tmp/quecprofiles_active"
# Function to log messages
log_message() {
local level="${2:-info}"
logger -t quecprofiles -p "daemon.$level" "status_check: $1"
}
# Function to output default "idle" JSON
output_idle_json() {
cat <<EOF
{
"status": "idle",
"message": "No active profile operations",
"profile": "unknown",
"progress": 0,
"timestamp": $(date +%s)
}
EOF
exit 0
}
# Check if status file exists
if [ -f "$STATUS_FILE" ]; then
# Check if file is not empty
if [ -s "$STATUS_FILE" ]; then
# Cat the entire file content (more reliable than grep)
status_content=$(cat "$STATUS_FILE")
# Log content for debugging
log_message "Status file content: $status_content" "debug"
# Check if it looks like valid JSON
if echo "$status_content" | grep -q "status"; then
# Output the status file content
cat "$STATUS_FILE"
# Extract status for logging only
status=$(echo "$status_content" | sed -n 's/.*"status":"\([^"]*\)".*/\1/p')
log_message "Status from file: $status" "info"
exit 0
else
log_message "Status file exists but not valid JSON" "warn"
fi
else
log_message "Status file exists but empty" "warn"
fi
fi
# If we get here, either no file exists or it's invalid/old
# Check if track file exists (as a fallback)
if [ -f "$TRACK_FILE" ]; then
status_info=$(cat "$TRACK_FILE")
status=$(echo "$status_info" | cut -d':' -f1)
profile=$(echo "$status_info" | cut -d':' -f2)
progress=$(echo "$status_info" | cut -d':' -f3)
# Make sure the message reflects the actual status
if [ "$status" = "success" ]; then
message="Profile successfully applied"
elif [ "$status" = "applying" ]; then
message="Profile operation in progress"
elif [ "$status" = "error" ]; then
message="Profile operation failed"
elif [ "$status" = "rebooting" ]; then
message="Device is rebooting to apply changes"
else
message="Profile operation status: $status"
fi
# Output JSON based on track file
cat <<EOF
{
"status": "$status",
"message": "$message",
"profile": "$profile",
"progress": $progress,
"timestamp": $(date +%s)
}
EOF
log_message "Retrieved status from track file: $status" "info"
exit 0
fi
# If no valid files found, output idle state
output_idle_json

View File

@@ -0,0 +1,154 @@
#!/bin/sh
# Location: /www/cgi-bin/quecmanager/profiles/list_profiles.sh
# Set content type to JSON and ensure blank line is output
echo "Content-type: application/json"
echo ""
# Function to log messages
log_message() {
local level="${2:-info}"
logger -t quecprofiles -p "daemon.$level" "list_profiles: $1"
# Also log to our error file
echo "[$(date)] $level: $1" >>/tmp/list_profiles_error.log
}
# Function to output JSON error response
output_error() {
local message="$1"
echo "{\"status\":\"error\",\"message\":\"$message\",\"profiles\":[]}"
log_message "$message" "error"
exit 1
}
# Function to sanitize string for JSON
sanitize_for_json() {
echo "$1" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | sed 's/\t/\\t/g' | tr -d '\r\n'
}
# Check if UCI command exists
if ! which uci >/dev/null 2>&1; then
output_error "UCI command not found"
fi
# Function to extract profiles from UCI config
get_profiles() {
log_message "Fetching profiles from UCI config"
# Check if UCI config exists
if [ ! -f /etc/config/quecprofiles ]; then
log_message "No profiles config found" "warn"
echo "{\"status\":\"success\",\"profiles\":[]}"
return 0
fi
# Start JSON output
local json_output=""
local first=1
local count=0
# Get all profile indices - make sure this succeeds
local indices=$(uci -q show quecprofiles | grep -o '@profile\[[0-9]*\]' | sort -u)
# Debug output
echo "Found indices: $indices" >>/tmp/list_profiles_error.log
if [ -z "$indices" ]; then
log_message "No profile indices found" "warn"
echo "{\"status\":\"success\",\"profiles\":[]}"
return 0
fi
# Process each profile
for idx in $indices; do
log_message "Processing profile index: $idx"
# Try different UCI get approaches
local name
name=$(uci -q get "quecprofiles.$idx.name" 2>/dev/null)
if [ -z "$name" ]; then
log_message "Failed to get name for $idx, trying alternative method" "warn"
local section=${idx#@profile[}
section=${section%]}
name=$(uci -q get "quecprofiles.@profile[$section].name" 2>/dev/null)
fi
# Get profile details
local iccid=$(uci -q get "quecprofiles.$idx.iccid" 2>/dev/null)
local imei=$(uci -q get "quecprofiles.$idx.imei" 2>/dev/null)
local apn=$(uci -q get "quecprofiles.$idx.apn" 2>/dev/null)
local pdp_type=$(uci -q get "quecprofiles.$idx.pdp_type" 2>/dev/null)
local lte_bands=$(uci -q get "quecprofiles.$idx.lte_bands" 2>/dev/null)
local sa_nr5g_bands=$(uci -q get "quecprofiles.$idx.sa_nr5g_bands" 2>/dev/null)
local nsa_nr5g_bands=$(uci -q get "quecprofiles.$idx.nsa_nr5g_bands" 2>/dev/null)
local network_type=$(uci -q get "quecprofiles.$idx.network_type" 2>/dev/null)
local ttl=$(uci -q get "quecprofiles.$idx.ttl" 2>/dev/null)
# Debug output
log_message "Retrieved for $idx: name=$name, iccid=$iccid, apn=$apn"
# Skip if missing required fields
if [ -z "$name" ] || [ -z "$iccid" ] || [ -z "$apn" ]; then
log_message "Skipping invalid profile: $idx (missing required fields)" "warn"
continue
fi
# Sanitize all values to ensure valid JSON
name=$(sanitize_for_json "$name")
iccid=$(sanitize_for_json "$iccid")
imei=$(sanitize_for_json "${imei:-""}")
apn=$(sanitize_for_json "$apn")
pdp_type=$(sanitize_for_json "${pdp_type:-"IPV4V6"}")
lte_bands=$(sanitize_for_json "${lte_bands:-""}")
sa_nr5g_bands=$(sanitize_for_json "${sa_nr5g_bands:-""}")
nsa_nr5g_bands=$(sanitize_for_json "${nsa_nr5g_bands:-""}")
network_type=$(sanitize_for_json "${network_type:-"LTE"}")
ttl=$(sanitize_for_json "${ttl:-0}")
# Create profile JSON
local profile_json="{"
profile_json="${profile_json}\"name\":\"${name}\","
profile_json="${profile_json}\"iccid\":\"${iccid}\","
profile_json="${profile_json}\"imei\":\"${imei}\","
profile_json="${profile_json}\"apn\":\"${apn}\","
profile_json="${profile_json}\"pdp_type\":\"${pdp_type}\","
profile_json="${profile_json}\"lte_bands\":\"${lte_bands}\","
profile_json="${profile_json}\"sa_nr5g_bands\":\"${sa_nr5g_bands}\","
profile_json="${profile_json}\"nsa_nr5g_bands\":\"${nsa_nr5g_bands}\","
profile_json="${profile_json}\"network_type\":\"${network_type}\","
profile_json="${profile_json}\"ttl\":\"${ttl}\""
profile_json="${profile_json}}"
# Add comma if not first
if [ $first -eq 0 ]; then
json_output="${json_output},"
else
first=0
fi
# Add profile to output
json_output="${json_output}${profile_json}"
count=$((count+1))
done
# Complete the JSON response
local response="{\"status\":\"success\",\"profiles\":[${json_output}]}"
# Save the response for debugging
echo "$response" > /tmp/list_profiles_response.json
echo "$response"
log_message "Found and returned $count profiles"
return 0
}
# Start fresh error log
echo "=== List Profiles Run $(date) ===" > /tmp/list_profiles_error.log
# Main execution
{
get_profiles
} || {
# Error handler
output_error "Failed to retrieve profiles"
}

View File

@@ -0,0 +1,355 @@
#!/bin/sh
# Location: /www/cgi-bin/quecmanager/profiles/quec_profile_create.sh
# Set content type to JSON
echo -n ""
echo "Content-type: application/json"
echo ""
# Function to log messages
log_message() {
local level="${2:-info}"
logger -t quecprofiles -p "daemon.$level" "create: $1"
}
# Function to output JSON response
output_json() {
local status="$1"
local message="$2"
local data="${3:-{}}"
printf '{"status":"%s","message":"%s","data":%s}\n' "$status" "$message" "$data"
exit 0
}
# Function to sanitize input
sanitize() {
echo "$1" | tr -d '\r\n' | sed 's/[^a-zA-Z0-9,.:_-]//g'
}
# Function to validate ICCID (simple check)
validate_iccid() {
local iccid="$1"
if [ -z "$iccid" ] || [ ${#iccid} -lt 10 ] || [ ${#iccid} -gt 20 ]; then
return 1
fi
# Check that it's only digits
if ! echo "$iccid" | grep -q '^[0-9]\+$'; then
return 1
fi
return 0
}
# Function to validate IMEI (simple check)
validate_imei() {
local imei="$1"
if [ -z "$imei" ]; then
return 0 # IMEI is optional
fi
if [ ${#imei} -ne 15 ] || ! echo "$imei" | grep -q '^[0-9]\+$'; then
return 1
fi
return 0
}
# Function to validate band list
validate_bands() {
local bands="$1"
if [ -z "$bands" ]; then
return 0 # Empty is valid
fi
# Check format (comma-separated numbers)
if ! echo "$bands" | grep -q '^[0-9]\+\(,[0-9]\+\)*$'; then
return 1
fi
return 0
}
# Function to validate network type
validate_network_type() {
local net_type="$1"
case "$net_type" in
"LTE" | "NR5G" | "LTE:NR5G")
return 0
;;
*)
return 1
;;
esac
}
# Function to validate PDP type
validate_pdp_type() {
local pdp_type="$1"
case "$pdp_type" in
"IP" | "IPV6" | "IPV4V6")
return 0
;;
*)
return 1
;;
esac
}
# Add function to validate TTL
validate_ttl() {
local ttl="$1"
if [ -z "$ttl" ]; then
return 0 # Empty is valid (will be treated as 0/disabled)
fi
# Check that TTL is a number between 0 and 255
if ! echo "$ttl" | grep -q '^[0-9]\+$' || [ "$ttl" -gt 255 ]; then
return 1
fi
return 0
}
# Function to check if a profile with the same name or ICCID already exists
check_duplicate_profile() {
local name="$1"
local iccid="$2"
# Check for duplicate name
local existing_name=$(uci -q show quecprofiles | grep ".name='$name'" | head -n 1)
if [ -n "$existing_name" ]; then
return 1
fi
# Check for duplicate ICCID
local existing_iccid=$(uci -q show quecprofiles | grep ".iccid='$iccid'" | head -n 1)
if [ -n "$existing_iccid" ]; then
return 2
fi
return 0
}
# Function to create new profile
create_profile() {
local name="$1"
local iccid="$2"
local imei="$3"
local apn="$4"
local pdp_type="$5"
local lte_bands="$6"
local sa_nr5g_bands="$7"
local nsa_nr5g_bands="$8"
local network_type="$9"
local ttl="${10}"
# Generate a unique ID for the profile
local profile_id="profile_$(date +%s)_$(head -c 4 /dev/urandom | hexdump -e '"%x"')"
# Add to UCI config
uci -q batch <<EOF
add quecprofiles profile
set quecprofiles.@profile[-1].name='$name'
set quecprofiles.@profile[-1].iccid='$iccid'
set quecprofiles.@profile[-1].imei='$imei'
set quecprofiles.@profile[-1].apn='$apn'
set quecprofiles.@profile[-1].pdp_type='$pdp_type'
set quecprofiles.@profile[-1].lte_bands='$lte_bands'
set quecprofiles.@profile[-1].sa_nr5g_bands='$sa_nr5g_bands'
set quecprofiles.@profile[-1].nsa_nr5g_bands='$nsa_nr5g_bands'
set quecprofiles.@profile[-1].network_type='$network_type'
set quecprofiles.@profile[-1].ttl='$ttl'
commit quecprofiles
EOF
# Check if the operation was successful
if [ $? -eq 0 ]; then
log_message "Successfully created profile '$name' for ICCID $iccid"
return 0
else
log_message "Failed to create profile '$name'" "error"
return 1
fi
}
# Output debug info
log_message "Received create profile request" "debug"
# Ensure UCI config exists
if [ ! -f /etc/config/quecprofiles ]; then
# Create initial config file
cat >/etc/config/quecprofiles <<EOF
config quecprofiles 'settings'
option check_interval '60'
option enable_autoswitch '1'
option apply_priority '20'
EOF
log_message "Created initial quecprofiles config file"
fi
# Get POST data
if [ "$REQUEST_METHOD" = "POST" ]; then
# Get content length
CONTENT_LENGTH=$(echo "$CONTENT_LENGTH" | tr -cd '0-9')
if [ -n "$CONTENT_LENGTH" ]; then
# Read POST data
POST_DATA=$(dd bs=1 count=$CONTENT_LENGTH 2>/dev/null)
# Debug log
log_message "Received POST data: $POST_DATA" "debug"
# Parse JSON with jsonfilter if available
if command -v jsonfilter >/dev/null 2>&1; then
name=$(echo "$POST_DATA" | jsonfilter -e '@.name' 2>/dev/null)
iccid=$(echo "$POST_DATA" | jsonfilter -e '@.iccid' 2>/dev/null)
imei=$(echo "$POST_DATA" | jsonfilter -e '@.imei' 2>/dev/null)
apn=$(echo "$POST_DATA" | jsonfilter -e '@.apn' 2>/dev/null)
pdp_type=$(echo "$POST_DATA" | jsonfilter -e '@.pdp_type' 2>/dev/null)
lte_bands=$(echo "$POST_DATA" | jsonfilter -e '@.lte_bands' 2>/dev/null)
sa_nr5g_bands=$(echo "$POST_DATA" | jsonfilter -e '@.sa_nr5g_bands' 2>/dev/null)
nsa_nr5g_bands=$(echo "$POST_DATA" | jsonfilter -e '@.nsa_nr5g_bands' 2>/dev/null)
network_type=$(echo "$POST_DATA" | jsonfilter -e '@.network_type' 2>/dev/null)
ttl=$(echo "$POST_DATA" | jsonfilter -e '@.ttl' 2>/dev/null)
log_message "Parsed JSON data for profile: $name" "debug"
else
# If jsonfilter is not available, try basic parsing
# This is less reliable but might work for simple cases
name=$(echo "$POST_DATA" | grep -o '"name":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
iccid=$(echo "$POST_DATA" | grep -o '"iccid":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
imei=$(echo "$POST_DATA" | grep -o '"imei":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
apn=$(echo "$POST_DATA" | grep -o '"apn":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
pdp_type=$(echo "$POST_DATA" | grep -o '"pdp_type":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
lte_bands=$(echo "$POST_DATA" | grep -o '"lte_bands":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
sa_nr5g_bands=$(echo "$POST_DATA" | grep -o '"sa_nr5g_bands":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
nsa_nr5g_bands=$(echo "$POST_DATA" | grep -o '"nsa_nr5g_bands":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
network_type=$(echo "$POST_DATA" | grep -o '"network_type":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
ttl=$(echo "$POST_DATA" | grep -o '"ttl":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
log_message "Basic parsing for profile: $name" "warn"
fi
else
log_message "No content length specified" "error"
output_json "error" "No data received"
fi
else
# URL parameters for GET requests (less secure, but supported for testing)
name=$(echo "$QUERY_STRING" | grep -o 'name=[^&]*' | cut -d'=' -f2)
iccid=$(echo "$QUERY_STRING" | grep -o 'iccid=[^&]*' | cut -d'=' -f2)
imei=$(echo "$QUERY_STRING" | grep -o 'imei=[^&]*' | cut -d'=' -f2)
apn=$(echo "$QUERY_STRING" | grep -o 'apn=[^&]*' | cut -d'=' -f2)
pdp_type=$(echo "$QUERY_STRING" | grep -o 'pdp_type=[^&]*' | cut -d'=' -f2)
lte_bands=$(echo "$QUERY_STRING" | grep -o 'lte_bands=[^&]*' | cut -d'=' -f2)
sa_nr5g_bands=$(echo "$QUERY_STRING" | grep -o 'sa_nr5g_bands=[^&]*' | cut -d'=' -f2)
nsa_nr5g_bands=$(echo "$QUERY_STRING" | grep -o 'nsa_nr5g_bands=[^&]*' | cut -d'=' -f2)
network_type=$(echo "$QUERY_STRING" | grep -o 'network_type=[^&]*' | cut -d'=' -f2)
ttl=$(echo "$QUERY_STRING" | grep -o 'ttl=[^&]*' | cut -d'=' -f2)
# URL decode values
name=$(echo "$name" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
iccid=$(echo "$iccid" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
imei=$(echo "$imei" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
apn=$(echo "$apn" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
pdp_type=$(echo "$pdp_type" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
lte_bands=$(echo "$lte_bands" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
sa_nr5g_bands=$(echo "$sa_nr5g_bands" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
nsa_nr5g_bands=$(echo "$nsa_nr5g_bands" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
network_type=$(echo "$network_type" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
ttl=$(echo "$ttl" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
log_message "Using URL parameters" "warn"
fi
# Sanitize inputs
name=$(sanitize "${name:-}")
iccid=$(sanitize "${iccid:-}")
imei=$(sanitize "${imei:-}")
apn=$(sanitize "${apn:-}")
pdp_type=$(sanitize "${pdp_type:-IP}")
lte_bands=$(sanitize "${lte_bands:-}")
sa_nr5g_bands=$(sanitize "${sa_nr5g_bands:-}")
nsa_nr5g_bands=$(sanitize "${nsa_nr5g_bands:-}")
network_type=$(sanitize "${network_type:-LTE}")
ttl=$(sanitize "${ttl:-0}") # Default to 0 (disabled)
# Output debug info
log_message "Creating profile: $name, ICCID: $iccid, IMEI: $imei, APN: $apn" "debug"
# Validate required inputs
if [ -z "$name" ]; then
log_message "Profile name is missing" "error"
output_json "error" "Profile name is required"
fi
if [ -z "$iccid" ]; then
log_message "ICCID is missing" "error"
output_json "error" "ICCID is required"
fi
if [ -z "$apn" ]; then
log_message "APN is missing" "error"
output_json "error" "APN is required"
fi
# Validate input formats
if ! validate_iccid "$iccid"; then
log_message "Invalid ICCID format: $iccid" "error"
output_json "error" "Invalid ICCID format. It should be 10-20 digits."
fi
if ! validate_imei "$imei"; then
log_message "Invalid IMEI format: $imei" "error"
output_json "error" "Invalid IMEI format. It should be exactly 15 digits."
fi
if ! validate_bands "$lte_bands"; then
log_message "Invalid LTE bands format: $lte_bands" "error"
output_json "error" "Invalid LTE bands format. Use comma-separated numbers (e.g., 1,3,7)"
fi
if ! validate_bands "$sa_nr5g_bands"; then
log_message "Invalid SA NR5G bands format: $sa_nr5g_bands" "error"
output_json "error" "Invalid SA NR5G bands format. Use comma-separated numbers (e.g., 41,78)"
fi
if ! validate_bands "$nsa_nr5g_bands"; then
log_message "Invalid NSA NR5G bands format: $nsa_nr5g_bands" "error"
output_json "error" "Invalid NSA NR5G bands format. Use comma-separated numbers (e.g., 1,79)"
fi
if ! validate_network_type "$network_type"; then
log_message "Invalid network type: $network_type" "error"
output_json "error" "Invalid network type. Use 'LTE', 'NR5G', or 'LTE:NR5G'"
fi
if ! validate_pdp_type "$pdp_type"; then
log_message "Invalid PDP type: $pdp_type" "error"
output_json "error" "Invalid PDP type. Use 'IP', 'IPV6', or 'IPV4V6'"
fi
if ! validate_ttl "$ttl"; then
log_message "Invalid TTL value: $ttl" "error"
output_json "error" "Invalid TTL value. It should be a number between 0 and 255."
fi
# Check for duplicates
check_duplicate_profile "$name" "$iccid"
dup_status=$?
if [ $dup_status -eq 1 ]; then
log_message "Duplicate profile name: $name" "error"
output_json "error" "A profile with this name already exists"
elif [ $dup_status -eq 2 ]; then
log_message "Duplicate ICCID: $iccid" "error"
output_json "error" "A profile with this ICCID already exists"
fi
# Create the profile
if create_profile "$name" "$iccid" "$imei" "$apn" "$pdp_type" "$lte_bands" "$sa_nr5g_bands" "$nsa_nr5g_bands" "$network_type" "$ttl"; then
# Trigger immediate profile application
touch "/tmp/quecprofiles_check"
chmod 644 "/tmp/quecprofiles_check"
log_message "Triggered immediate profile check after creation" "info"
# Create profile data JSON for return - WITHOUT outer curly braces
profile_data="\"name\":\"$name\",\"iccid\":\"$iccid\",\"imei\":\"$imei\",\"apn\":\"$apn\",\"pdp_type\":\"$pdp_type\",\"lte_bands\":\"$lte_bands\",\"sa_nr5g_bands\":\"$sa_nr5g_bands\",\"nsa_nr5g_bands\":\"$nsa_nr5g_bands\",\"network_type\":\"$network_type\",\"ttl\":\"$ttl\""
# Wrap the data field in curly braces inside output_json
output_json "success" "Profile created successfully" "{$profile_data}"
else
output_json "error" "Failed to create profile. Please check system logs."
fi

View File

@@ -0,0 +1,138 @@
#!/bin/sh
# Location: /www/cgi-bin/quecmanager/profiles/quec_profile_delete.sh
# Set content type to JSON
echo -n ""
echo "Content-type: application/json"
echo ""
# Function to log messages
log_message() {
local level="${2:-info}"
logger -t quecprofiles -p "daemon.$level" "delete: $1"
}
# Function to output JSON response
output_json() {
local status="$1"
local message="$2"
local data="${3:-{}}"
printf '{"status":"%s","message":"%s","data":%s}\n' "$status" "$message" "$data"
exit 0
}
# Function to sanitize input
sanitize() {
echo "$1" | tr -d '\r\n' | sed 's/[^a-zA-Z0-9,.:_-]//g'
}
# Function to find profile by ICCID
find_profile_by_iccid() {
local iccid="$1"
# Get all profile indices
local profile_indices=$(uci show quecprofiles | grep -o '@profile\[[0-9]\+\]' | sort -u)
for profile_index in $profile_indices; do
local current_iccid=$(uci -q get quecprofiles.$profile_index.iccid)
if [ "$current_iccid" = "$iccid" ]; then
echo "$profile_index"
return 0
fi
done
return 1
}
# Function to delete a profile
delete_profile() {
local profile_index="$1"
local profile_name=$(uci -q get quecprofiles.$profile_index.name)
# Delete the profile from UCI config
uci -q batch <<EOF
delete quecprofiles.$profile_index
commit quecprofiles
EOF
# Check if the operation was successful
if [ $? -eq 0 ]; then
log_message "Successfully deleted profile '$profile_name'" "info"
return 0
else
log_message "Failed to delete profile '$profile_name'" "error"
return 1
fi
}
# Output debug info
log_message "Received delete profile request" "debug"
# Ensure UCI config exists
if [ ! -f /etc/config/quecprofiles ]; then
log_message "quecprofiles config does not exist" "error"
output_json "error" "Configuration file not found"
fi
# Get ICCID from request
iccid=""
if [ "$REQUEST_METHOD" = "POST" ]; then
# Get content length
CONTENT_LENGTH=$(echo "$CONTENT_LENGTH" | tr -cd '0-9')
if [ -n "$CONTENT_LENGTH" ]; then
# Read POST data
POST_DATA=$(dd bs=1 count=$CONTENT_LENGTH 2>/dev/null)
# Debug log
log_message "Received POST data: $POST_DATA" "debug"
# Parse JSON with jsonfilter if available
if command -v jsonfilter >/dev/null 2>&1; then
iccid=$(echo "$POST_DATA" | jsonfilter -e '@.iccid' 2>/dev/null)
else
# If jsonfilter is not available, try basic parsing
iccid=$(echo "$POST_DATA" | grep -o '"iccid":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
fi
else
log_message "No content length specified" "error"
output_json "error" "No data received"
fi
elif [ -n "$QUERY_STRING" ]; then
# URL parameters for GET or DELETE requests
iccid=$(echo "$QUERY_STRING" | grep -o 'iccid=[^&]*' | cut -d'=' -f2)
# URL decode value
iccid=$(echo "$iccid" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
log_message "Using URL parameter: iccid=$iccid" "debug"
fi
# Sanitize input
iccid=$(sanitize "${iccid:-}")
# Validate ICCID
if [ -z "$iccid" ]; then
log_message "ICCID is missing" "error"
output_json "error" "ICCID is required to identify the profile"
fi
# Find profile to delete
profile_index=$(find_profile_by_iccid "$iccid")
if [ $? -ne 0 ]; then
log_message "Profile with ICCID $iccid not found" "error"
output_json "error" "Profile not found"
fi
# Get profile info for response
profile_name=$(uci -q get quecprofiles.$profile_index.name)
# Delete the profile
if delete_profile "$profile_index"; then
log_message "Profile deleted successfully: $profile_name" "info"
output_json "success" "Profile deleted successfully" "{\"iccid\":\"$iccid\",\"name\":\"$profile_name\"}"
else
log_message "Failed to delete profile: $profile_name" "error"
output_json "error" "Failed to delete profile. Please check system logs."
fi

View File

@@ -0,0 +1,392 @@
#!/bin/sh
# Location: /www/cgi-bin/quecmanager/profiles/quec_profile_edit.sh
# Set content type to JSON
echo -n ""
echo "Content-type: application/json"
echo ""
# Configuration
CHECK_TRIGGER="/tmp/quecprofiles_check"
APPLIED_FLAG="/tmp/quecprofiles_applied"
# Function to log messages
log_message() {
local level="${2:-info}"
logger -t quecprofiles -p "daemon.$level" "edit: $1"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] edit: $1" >>/tmp/quec_profile_edit.log
}
# Function to output JSON response
output_json() {
local status="$1"
local message="$2"
local data="${3:-{}}"
# Debug log to file only
echo "Generating JSON response: status=$status, message=$message" >>/tmp/quec_profile_debug.log
echo "Data payload: $data" >>/tmp/quec_profile_debug.log
# Use printf for consistent output without newlines or extra characters
printf '{"status":"%s","message":"%s","data":%s}' "$status" "$message" "$data"
# Add debug marker at end of JSON
echo "" >>/tmp/quec_profile_debug.log
echo "JSON response generated at $(date)" >>/tmp/quec_profile_debug.log
exit 0
}
# Function to sanitize input
sanitize() {
echo "$1" | tr -d '\r\n' | sed 's/[^a-zA-Z0-9,.:_-]//g'
}
# Function to validate ICCID (simple check)
validate_iccid() {
local iccid="$1"
if [ -z "$iccid" ] || [ ${#iccid} -lt 10 ] || [ ${#iccid} -gt 20 ]; then
return 1
fi
# Check that it's only digits
if ! echo "$iccid" | grep -q '^[0-9]\+$'; then
return 1
fi
return 0
}
# Function to validate IMEI (simple check)
validate_imei() {
local imei="$1"
if [ -z "$imei" ]; then
return 0 # IMEI is optional
fi
if [ ${#imei} -ne 15 ] || ! echo "$imei" | grep -q '^[0-9]\+$'; then
return 1
fi
return 0
}
# Function to validate band list
validate_bands() {
local bands="$1"
if [ -z "$bands" ]; then
return 0 # Empty is valid
fi
# Check format (comma-separated numbers)
if ! echo "$bands" | grep -q '^[0-9]\+\(,[0-9]\+\)*$'; then
return 1
fi
return 0
}
# Function to validate network type
validate_network_type() {
local net_type="$1"
case "$net_type" in
"LTE" | "NR5G" | "LTE:NR5G")
return 0
;;
*)
return 1
;;
esac
}
# Function to validate PDP type
validate_pdp_type() {
local pdp_type="$1"
case "$pdp_type" in
"IP" | "IPV6" | "IPV4V6")
return 0
;;
*)
return 1
;;
esac
}
validate_ttl() {
local ttl="$1"
if [ -z "$ttl" ]; then
return 0 # Empty is valid (will be treated as 0/disabled)
fi
# Check that TTL is a number between 0 and 255
if ! echo "$ttl" | grep -q '^[0-9]\+$' || [ "$ttl" -gt 255 ]; then
return 1
fi
return 0
}
# Function to check if a profile with given ICCID exists
find_profile_by_iccid() {
local iccid="$1"
# Get all profile indices
local profile_indices=$(uci show quecprofiles | grep -o '@profile\[[0-9]\+\]' | sort -u)
for profile_index in $profile_indices; do
local current_iccid=$(uci -q get quecprofiles.$profile_index.iccid)
if [ "$current_iccid" = "$iccid" ]; then
echo "$profile_index"
return 0
fi
done
return 1
}
# Function to check for duplicate name (excluding current profile)
check_duplicate_name() {
local name="$1"
local current_iccid="$2"
local profile_indices=$(uci show quecprofiles | grep -o '@profile\[[0-9]\+\]' | sort -u)
for profile_index in $profile_indices; do
local iccid=$(uci -q get quecprofiles.$profile_index.iccid)
local profile_name=$(uci -q get quecprofiles.$profile_index.name)
# Skip the current profile we're editing
if [ "$iccid" = "$current_iccid" ]; then
continue
fi
if [ "$profile_name" = "$name" ]; then
return 0 # Found duplicate
fi
done
return 1 # No duplicate
}
# Function to update an existing profile
update_profile() {
local profile_index="$1"
local name="$2"
local imei="$3"
local apn="$4"
local pdp_type="$5"
local lte_bands="$6"
local sa_nr5g_bands="$7"
local nsa_nr5g_bands="$8"
local network_type="$9"
local ttl="${10}"
# Update the profile in UCI config
uci -q batch <<EOF
set quecprofiles.$profile_index.name='$name'
set quecprofiles.$profile_index.imei='$imei'
set quecprofiles.$profile_index.apn='$apn'
set quecprofiles.$profile_index.pdp_type='$pdp_type'
set quecprofiles.$profile_index.lte_bands='$lte_bands'
set quecprofiles.$profile_index.sa_nr5g_bands='$sa_nr5g_bands'
set quecprofiles.$profile_index.nsa_nr5g_bands='$nsa_nr5g_bands'
set quecprofiles.$profile_index.network_type='$network_type'
set quecprofiles.$profile_index.ttl='$ttl'
commit quecprofiles
EOF
# Check if the operation was successful
if [ $? -eq 0 ]; then
log_message "Successfully updated profile '$name'" "info"
# Remove the applied flag file to force reapplication on next check
rm -f "$APPLIED_FLAG"
# Touch the check trigger file to force daemon to check ASAP
touch "$CHECK_TRIGGER"
log_message "Triggered profile check for updated profile '$name'" "info"
return 0
else
log_message "Failed to update profile '$name'" "error"
return 1
fi
}
# Output debug info
log_message "Received edit profile request" "debug"
# Ensure UCI config exists
if [ ! -f /etc/config/quecprofiles ]; then
log_message "quecprofiles config does not exist" "error"
output_json "error" "Configuration file not found"
fi
# Get POST data
if [ "$REQUEST_METHOD" = "POST" ]; then
# Get content length
CONTENT_LENGTH=$(echo "$CONTENT_LENGTH" | tr -cd '0-9')
if [ -n "$CONTENT_LENGTH" ]; then
# Read POST data
POST_DATA=$(dd bs=1 count=$CONTENT_LENGTH 2>/dev/null)
# Debug log
log_message "Received POST data: $POST_DATA" "debug"
# Parse JSON with jsonfilter if available
if command -v jsonfilter >/dev/null 2>&1; then
iccid=$(echo "$POST_DATA" | jsonfilter -e '@.iccid' 2>/dev/null)
name=$(echo "$POST_DATA" | jsonfilter -e '@.name' 2>/dev/null)
imei=$(echo "$POST_DATA" | jsonfilter -e '@.imei' 2>/dev/null)
apn=$(echo "$POST_DATA" | jsonfilter -e '@.apn' 2>/dev/null)
pdp_type=$(echo "$POST_DATA" | jsonfilter -e '@.pdp_type' 2>/dev/null)
lte_bands=$(echo "$POST_DATA" | jsonfilter -e '@.lte_bands' 2>/dev/null)
sa_nr5g_bands=$(echo "$POST_DATA" | jsonfilter -e '@.sa_nr5g_bands' 2>/dev/null)
nsa_nr5g_bands=$(echo "$POST_DATA" | jsonfilter -e '@.nsa_nr5g_bands' 2>/dev/null)
network_type=$(echo "$POST_DATA" | jsonfilter -e '@.network_type' 2>/dev/null)
ttl=$(echo "$POST_DATA" | jsonfilter -e '@.ttl' 2>/dev/null)
log_message "Parsed JSON data for profile: $name" "debug"
else
# If jsonfilter is not available, try basic parsing
# This is less reliable but might work for simple cases
iccid=$(echo "$POST_DATA" | grep -o '"iccid":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
name=$(echo "$POST_DATA" | grep -o '"name":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
imei=$(echo "$POST_DATA" | grep -o '"imei":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
apn=$(echo "$POST_DATA" | grep -o '"apn":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
pdp_type=$(echo "$POST_DATA" | grep -o '"pdp_type":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
lte_bands=$(echo "$POST_DATA" | grep -o '"lte_bands":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
sa_nr5g_bands=$(echo "$POST_DATA" | grep -o '"sa_nr5g_bands":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
nsa_nr5g_bands=$(echo "$POST_DATA" | grep -o '"nsa_nr5g_bands":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
network_type=$(echo "$POST_DATA" | grep -o '"network_type":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
ttl=$(echo "$POST_DATA" | grep -o '"ttl":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
log_message "Basic parsing for profile: $name" "warn"
fi
else
log_message "No content length specified" "error"
output_json "error" "No data received"
fi
else
# URL parameters for GET requests (less secure, but supported for testing)
iccid=$(echo "$QUERY_STRING" | grep -o 'iccid=[^&]*' | cut -d'=' -f2)
name=$(echo "$QUERY_STRING" | grep -o 'name=[^&]*' | cut -d'=' -f2)
imei=$(echo "$QUERY_STRING" | grep -o 'imei=[^&]*' | cut -d'=' -f2)
apn=$(echo "$QUERY_STRING" | grep -o 'apn=[^&]*' | cut -d'=' -f2)
pdp_type=$(echo "$QUERY_STRING" | grep -o 'pdp_type=[^&]*' | cut -d'=' -f2)
lte_bands=$(echo "$QUERY_STRING" | grep -o 'lte_bands=[^&]*' | cut -d'=' -f2)
sa_nr5g_bands=$(echo "$QUERY_STRING" | grep -o 'sa_nr5g_bands=[^&]*' | cut -d'=' -f2)
nsa_nr5g_bands=$(echo "$QUERY_STRING" | grep -o 'nsa_nr5g_bands=[^&]*' | cut -d'=' -f2)
network_type=$(echo "$QUERY_STRING" | grep -o 'network_type=[^&]*' | cut -d'=' -f2)
ttl=$(echo "$QUERY_STRING" | grep -o 'ttl=[^&]*' | cut -d'=' -f2)
# URL decode values
iccid=$(echo "$iccid" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
name=$(echo "$name" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
imei=$(echo "$imei" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
apn=$(echo "$apn" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
pdp_type=$(echo "$pdp_type" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
lte_bands=$(echo "$lte_bands" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
sa_nr5g_bands=$(echo "$sa_nr5g_bands" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
nsa_nr5g_bands=$(echo "$nsa_nr5g_bands" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
network_type=$(echo "$network_type" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
ttl=$(echo "$ttl" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
log_message "Using URL parameters" "warn"
fi
# Sanitize inputs
iccid=$(sanitize "${iccid:-}")
name=$(sanitize "${name:-}")
imei=$(sanitize "${imei:-}")
apn=$(sanitize "${apn:-}")
pdp_type=$(sanitize "${pdp_type:-IP}")
lte_bands=$(sanitize "${lte_bands:-}")
sa_nr5g_bands=$(sanitize "${sa_nr5g_bands:-}")
nsa_nr5g_bands=$(sanitize "${nsa_nr5g_bands:-}")
network_type=$(sanitize "${network_type:-LTE}")
ttl=$(sanitize "${ttl:-0}") # Default to 0 (disabled)
# Output debug info
log_message "Editing profile: $name, ICCID: $iccid, IMEI: $imei, APN: $apn" "debug"
# Validate required inputs
if [ -z "$iccid" ]; then
log_message "ICCID is missing" "error"
output_json "error" "ICCID is required to identify the profile"
fi
if [ -z "$name" ]; then
log_message "Profile name is missing" "error"
output_json "error" "Profile name is required"
fi
if [ -z "$apn" ]; then
log_message "APN is missing" "error"
output_json "error" "APN is required"
fi
# Validate input formats
if ! validate_iccid "$iccid"; then
log_message "Invalid ICCID format: $iccid" "error"
output_json "error" "Invalid ICCID format. It should be 10-20 digits."
fi
if ! validate_imei "$imei"; then
log_message "Invalid IMEI format: $imei" "error"
output_json "error" "Invalid IMEI format. It should be exactly 15 digits."
fi
if ! validate_bands "$lte_bands"; then
log_message "Invalid LTE bands format: $lte_bands" "error"
output_json "error" "Invalid LTE bands format. Use comma-separated numbers (e.g., 1,3,7)"
fi
if ! validate_bands "$sa_nr5g_bands"; then
log_message "Invalid SA NR5G bands format: $sa_nr5g_bands" "error"
output_json "error" "Invalid SA NR5G bands format. Use comma-separated numbers (e.g., 41,78)"
fi
if ! validate_bands "$nsa_nr5g_bands"; then
log_message "Invalid NSA NR5G bands format: $nsa_nr5g_bands" "error"
output_json "error" "Invalid NSA NR5G bands format. Use comma-separated numbers (e.g., 1,79)"
fi
if ! validate_network_type "$network_type"; then
log_message "Invalid network type: $network_type" "error"
output_json "error" "Invalid network type. Use 'LTE', 'NR5G', or 'LTE:NR5G'"
fi
if ! validate_pdp_type "$pdp_type"; then
log_message "Invalid PDP type: $pdp_type" "error"
output_json "error" "Invalid PDP type. Use 'IP', 'IPV6', or 'IPV4V6'"
fi
if ! validate_ttl "$ttl"; then
log_message "Invalid TTL value: $ttl" "error"
output_json "error" "Invalid TTL value. It should be a number between 0 and 255."
fi
# Find profile to edit
profile_index=$(find_profile_by_iccid "$iccid")
if [ $? -ne 0 ]; then
log_message "Profile with ICCID $iccid not found" "error"
output_json "error" "Profile not found"
fi
# Check for duplicate name
if check_duplicate_name "$name" "$iccid"; then
log_message "Duplicate profile name: $name" "error"
output_json "error" "A profile with this name already exists"
fi
# Update profile
if update_profile "$profile_index" "$name" "$imei" "$apn" "$pdp_type" "$lte_bands" "$nr5g_bands" "$network_type"; then
# Trigger immediate profile application
touch "/tmp/quecprofiles_check"
chmod 644 "/tmp/quecprofiles_check"
log_message "Triggered immediate profile check after update" "info"
# Create a clean JSON response with properly escaped quotes
printf '{"status":"success","message":"Profile updated successfully","data":{"name":"%s","iccid":"%s","imei":"%s","apn":"%s","pdp_type":"%s","lte_bands":"%s","nr5g_bands":"%s","network_type":"%s"}}' \
"$name" "$iccid" "$imei" "$apn" "$pdp_type" "$lte_bands" "$nr5g_bands" "$network_type"
log_message "Profile updated successfully: $name" "info"
# Note: The conditional trigger is replaced with the direct trigger above
else
printf '{"status":"error","message":"Failed to update profile. Please check system logs."}'
log_message "Failed to update profile: $name" "error"
fi

View File

@@ -0,0 +1,38 @@
#!/bin/sh
# Set content type for JSON response
echo "Content-type: application/json"
echo ""
# Get system uptime in seconds from /proc/uptime
read uptime idle < /proc/uptime
uptime=${uptime%.*} # Remove decimal part
# Calculate days, hours, minutes, seconds
days=$((uptime/86400))
hours=$(((uptime%86400)/3600))
minutes=$(((uptime%3600)/60))
seconds=$((uptime%60))
# Format uptime string
uptime_str=""
[ $days -gt 0 ] && uptime_str="${days}d "
[ $hours -gt 0 ] && uptime_str="${uptime_str}${hours}h "
[ $minutes -gt 0 ] && uptime_str="${uptime_str}${minutes}m "
uptime_str="${uptime_str}${seconds}s"
# Create and output JSON response
cat << EOF
{
"status": "success",
"timestamp": "$(date -Iseconds)",
"uptime": {
"total_seconds": $uptime,
"days": $days,
"hours": $hours,
"minutes": $minutes,
"seconds": $seconds,
"formatted": "${uptime_str}"
}
}
EOF

View File

@@ -0,0 +1,64 @@
#!/bin/sh
# Send CGI headers first
echo "Content-Type: application/json"
echo "Cache-Control: no-cache"
echo
# Initialize variables for file paths
APN_SCRIPT="/etc/quecmanager/apn_profile/apnProfiles.sh"
IMEI_SCRIPT="/etc/quecmanager/imei_profile/imeiProfiles.sh"
# Function to output JSON
output_json() {
local status="$1"
local message="$2"
echo "{\"status\": \"$status\", \"message\": \"$message\"}"
}
# Function to execute script if it exists
execute_if_exists() {
local script_path="$1"
if [ -f "$script_path" ] && [ -x "$script_path" ]; then
$script_path >/dev/null 2>&1
return $?
fi
return 2
}
# Main execution
main() {
scripts_executed=0
has_error=0
# Try to execute APN script
execute_if_exists "$APN_SCRIPT"
apn_result=$?
if [ $apn_result -eq 0 ]; then
scripts_executed=$(($scripts_executed + 1))
elif [ $apn_result -eq 1 ]; then
has_error=1
fi
# Try to execute IMEI script
execute_if_exists "$IMEI_SCRIPT"
imei_result=$?
if [ $imei_result -eq 0 ]; then
scripts_executed=$(($scripts_executed + 1))
elif [ $imei_result -eq 1 ]; then
has_error=1
fi
# Output appropriate message based on results
if [ $scripts_executed -eq 0 ]; then
output_json "info" "No scripts to restart"
elif [ $has_error -eq 1 ]; then
output_json "error" "Error executing one or more scripts"
else
output_json "success" "Scripts restarted successfully"
fi
}
# Run main function
main

View File

@@ -0,0 +1,672 @@
#!/bin/sh
# AT Queue Manager for OpenWRT with Preemption Support and Token System
# Located in /www/cgi-bin/services/at_queue_manager
# Constants
QUEUE_DIR="/tmp/at_queue"
QUEUE_FILE="$QUEUE_DIR/queue"
ACTIVE_FILE="$QUEUE_DIR/active"
RESULTS_DIR="$QUEUE_DIR/results"
LOCK_DIR="$QUEUE_DIR/lock"
TOKEN_FILE="$QUEUE_DIR/token"
MAX_TIMEOUT=240
CLEANUP_INTERVAL=300 # 5 minutes in seconds
RESULTS_MAX_AGE=3600 # 1 hour in seconds
POLL_INTERVAL=0.01
PREEMPTION_THRESHOLD=2 # 3 seconds threshold for preemption
TOKEN_TIMEOUT=30 # seconds before token expires
# Utility function for JSON escaping
escape_json() {
printf '%s' "$1" | awk '
BEGIN { RS="\n"; ORS="\\n" }
{
gsub(/\\/, "\\\\")
gsub(/"/, "\\\"")
gsub(/\r/, "")
gsub(/\t/, "\\t")
gsub(/\f/, "\\f")
gsub(/\b/, "\\b")
print
}
' | sed 's/\\n$//'
}
# Exclusive lock functions
acquire_lock() {
local timeout=10
local attempt=0
while [ $attempt -lt $timeout ]; do
if mkdir "$LOCK_DIR" 2>/dev/null; then
logger -t at_queue -p daemon.debug "Lock acquired"
return 0
fi
sleep 0.1
attempt=$((attempt + 1))
done
logger -t at_queue -p daemon.error "Failed to acquire lock after $timeout attempts"
return 1
}
release_lock() {
if [ -d "$LOCK_DIR" ]; then
rmdir "$LOCK_DIR" 2>/dev/null
logger -t at_queue -p daemon.debug "Lock released"
return 0
fi
logger -t at_queue -p daemon.error "Lock directory doesn't exist"
return 1
}
# Ensure required directories exist
init_queue_system() {
mkdir -p "$QUEUE_DIR" "$RESULTS_DIR"
touch "$QUEUE_FILE"
chmod 755 "$QUEUE_DIR"
chmod 644 "$QUEUE_FILE"
chmod 755 "$RESULTS_DIR"
logger -t at_queue -p daemon.info "Queue system initialized"
}
# Cleanup old results and tracking files
cleanup_old_results() {
local current_time=$(date +%s)
# Clean up old execution tracking files
find "$QUEUE_DIR" -name "pid.*" -type f -mmin +60 -delete 2>/dev/null
find "$QUEUE_DIR" -name "*.exit" -type f -mmin +60 -delete 2>/dev/null
find "$QUEUE_DIR" -name "start_time.*" -type f -mmin +60 -delete 2>/dev/null
logger -t at_queue -p daemon.debug "Cleaned up old tracking files"
# Use find with -delete and basic timestamp check for OpenWRT
find "$RESULTS_DIR" -name "*.json" -type f -mmin +60 -delete 2>/dev/null || {
# Fallback method if find fails
for file in "$RESULTS_DIR"/*.json; do
[ -f "$file" ] || continue
local file_time=$(stat -c %Y "$file")
if [ $((current_time - file_time)) -gt $RESULTS_MAX_AGE ]; then
rm -f "$file"
fi
done
}
# Check for expired token
if [ -f "$TOKEN_FILE" ]; then
local token_time=$(cat "$TOKEN_FILE" | jsonfilter -e '@.timestamp')
if [ $((current_time - token_time)) -gt $TOKEN_TIMEOUT ]; then
local token_holder=$(cat "$TOKEN_FILE" | jsonfilter -e '@.id')
logger -t at_queue -p daemon.warn "Removing expired token from $token_holder"
rm -f "$TOKEN_FILE"
fi
fi
logger -t at_queue -p daemon.info "Cleanup: Removed files older than 1 hour"
}
# Generate unique command ID
generate_command_id() {
echo "$(date +%s)_$(head -c 8 /dev/urandom | hexdump -v -e '1/1 "%02x"')"
}
# Start tracking command execution time
start_execution_tracking() {
local cmd_id="$1"
local pid="$2"
local start_time=$(date +%s)
echo "$start_time" > "$QUEUE_DIR/start_time.$cmd_id"
echo "$pid" > "$QUEUE_DIR/pid.$cmd_id"
chmod 644 "$QUEUE_DIR/start_time.$cmd_id"
chmod 644 "$QUEUE_DIR/pid.$cmd_id"
logger -t at_queue -p daemon.debug "Started tracking command $cmd_id (PID: $pid)"
}
# Check if running command should be preempted
should_preempt() {
local current_cmd_id="$1"
local new_priority="$2"
if [ ! -f "$QUEUE_DIR/start_time.$current_cmd_id" ]; then
logger -t at_queue -p daemon.debug "No start time found for $current_cmd_id"
return 1
fi
local start_time=$(cat "$QUEUE_DIR/start_time.$current_cmd_id")
local current_time=$(date +%s)
local execution_time=$((current_time - start_time))
# Get current command's priority
local current_priority
if [ -f "$ACTIVE_FILE" ]; then
current_priority=$(cat "$ACTIVE_FILE" | jsonfilter -e '@.priority')
else
logger -t at_queue -p daemon.debug "No active command found"
return 1
fi
if [ $execution_time -gt $PREEMPTION_THRESHOLD ] && [ $new_priority -lt $current_priority ]; then
logger -t at_queue -p daemon.info "Command $current_cmd_id (priority $current_priority) running for ${execution_time}s is eligible for preemption by priority $new_priority"
return 0
fi
logger -t at_queue -p daemon.debug "Command $current_cmd_id not eligible for preemption (time: ${execution_time}s, current priority: $current_priority, new priority: $new_priority)"
return 1
}
# Handle command preemption
preempt_command() {
local cmd_id="$1"
local pid_file="$QUEUE_DIR/pid.$cmd_id"
if [ -f "$pid_file" ]; then
local pid=$(cat "$pid_file")
logger -t at_queue -p daemon.info "Preempting command $cmd_id (PID: $pid)"
# Send SIGTERM first
kill -TERM $pid 2>/dev/null
# Brief wait for graceful termination
sleep 0.1
# Force kill if still running
if kill -0 $pid 2>/dev/null; then
kill -KILL $pid 2>/dev/null
logger -t at_queue -p daemon.warn "Forced termination of command $cmd_id"
fi
# Record preemption result
write_preemption_result "$cmd_id"
# Cleanup command files
rm -f "$pid_file" "$QUEUE_DIR/start_time.$cmd_id" "$QUEUE_DIR/$cmd_id.exit"
[ -f "$ACTIVE_FILE" ] && rm -f "$ACTIVE_FILE"
logger -t at_queue -p daemon.info "Command $cmd_id preemption complete"
return 0
fi
logger -t at_queue -p daemon.warn "No PID file found for command $cmd_id"
return 1
}
# Record result for preempted command
write_preemption_result() {
local cmd_id="$1"
local end_time=$(date +%s%3N)
local start_time
if [ -f "$QUEUE_DIR/start_time.$cmd_id" ]; then
start_time=$(cat "$QUEUE_DIR/start_time.$cmd_id")000
else
start_time=$end_time
fi
local duration=$((end_time - start_time))
local command_text=$(cat "$ACTIVE_FILE" | jsonfilter -e '@.command')
local response=$(cat << EOF
{
"command": {
"id": "$cmd_id",
"text": "$(escape_json "$command_text")",
"timestamp": "$(date -Iseconds)"
},
"response": {
"status": "preempted",
"raw_output": "Command preempted by higher priority task",
"completion_time": "$end_time",
"duration_ms": $duration
}
}
EOF
)
printf "%s" "$response" > "$RESULTS_DIR/$cmd_id.json"
chmod 644 "$RESULTS_DIR/$cmd_id.json"
logger -t at_queue -p daemon.info "Recorded preemption result for command $cmd_id (duration: ${duration}ms)"
}
# Request a token for direct sms_tool execution
request_token() {
local requestor_id="$1"
local priority="${2:-10}"
local timeout="${3:-10}"
# Acquire lock first
if ! acquire_lock; then
logger -t at_queue -p daemon.error "Failed to acquire lock for token request"
echo "{\"error\":\"Could not acquire lock\",\"status\":\"denied\"}"
return 1
fi
# Check if token file exists (someone else has the token)
if [ -f "$TOKEN_FILE" ]; then
local current_holder=$(cat "$TOKEN_FILE" | jsonfilter -e '@.id')
local current_priority=$(cat "$TOKEN_FILE" | jsonfilter -e '@.priority')
local timestamp=$(cat "$TOKEN_FILE" | jsonfilter -e '@.timestamp')
local current_time=$(date +%s)
# Check for expired token (> TOKEN_TIMEOUT seconds old)
if [ $((current_time - timestamp)) -gt $TOKEN_TIMEOUT ]; then
logger -t at_queue -p daemon.warn "Found expired token from $current_holder, releasing"
rm -f "$TOKEN_FILE"
# Check for priority preemption
elif [ $priority -lt $current_priority ]; then
logger -t at_queue -p daemon.info "Preempting token from $current_holder (priority: $current_priority) for $requestor_id (priority: $priority)"
rm -f "$TOKEN_FILE"
else
# Token in use and cannot be preempted
release_lock
echo "{\"status\":\"denied\",\"holder\":\"$current_holder\",\"priority\":$current_priority}"
return 1
fi
fi
# Also check if there's an active command from the queue
if [ -f "$ACTIVE_FILE" ]; then
local active_id=$(cat "$ACTIVE_FILE" | jsonfilter -e '@.id')
local active_priority=$(cat "$ACTIVE_FILE" | jsonfilter -e '@.priority')
# Only preempt if priority is higher
if [ $priority -ge $active_priority ]; then
release_lock
echo "{\"status\":\"denied\",\"holder\":\"$active_id\",\"priority\":$active_priority}"
return 1
fi
logger -t at_queue -p daemon.info "Direct execution with higher priority than active queue command"
fi
# Grant token
local token_data="{\"id\":\"$requestor_id\",\"priority\":$priority,\"timestamp\":$(date +%s)}"
echo "$token_data" > "$TOKEN_FILE"
chmod 644 "$TOKEN_FILE"
release_lock
echo "{\"status\":\"granted\",\"id\":\"$requestor_id\",\"timeout\":$timeout}"
return 0
}
# Release a previously acquired token
release_token() {
local requestor_id="$1"
if ! acquire_lock; then
logger -t at_queue -p daemon.error "Failed to acquire lock for token release"
return 1
fi
if [ -f "$TOKEN_FILE" ]; then
local current_holder=$(cat "$TOKEN_FILE" | jsonfilter -e '@.id')
if [ "$current_holder" = "$requestor_id" ]; then
rm -f "$TOKEN_FILE"
logger -t at_queue -p daemon.debug "Token released by $requestor_id"
release_lock
echo "{\"status\":\"released\"}"
return 0
else
logger -t at_queue -p daemon.warn "Token release attempted by $requestor_id but held by $current_holder"
fi
else
logger -t at_queue -p daemon.warn "Token release attempted but no token exists"
fi
release_lock
echo "{\"status\":\"not_found\"}"
return 1
}
# Add command to queue with preemption support
enqueue_command() {
local cmd="$1"
local priority="${2:-10}"
local cmd_id=$(generate_command_id)
local timestamp=$(date -Iseconds)
# Ensure queue directory exists
[ ! -d "$QUEUE_DIR" ] && init_queue_system
logger -t at_queue -p daemon.info "Enqueuing command: $cmd (priority: $priority, id: $cmd_id)"
# Acquire lock for queue modification
if ! acquire_lock; then
logger -t at_queue -p daemon.error "Failed to acquire lock for enqueuing command"
echo "{\"error\":\"Queue lock acquisition failed\",\"command\":\"$cmd\"}"
return 1
fi
# Check for active command that can be preempted
if [ -f "$ACTIVE_FILE" ]; then
local active_cmd_id=$(cat "$ACTIVE_FILE" | jsonfilter -e '@.id')
if should_preempt "$active_cmd_id" "$priority"; then
preempt_command "$active_cmd_id"
fi
fi
# Create command entry
local entry="{\"id\":\"$cmd_id\",\"command\":\"$(escape_json "$cmd")\",\"priority\":$priority,\"timestamp\":\"$timestamp\"}"
if [ "$priority" = "1" ]; then
# High priority - prepend to queue
local temp_file=$(mktemp)
echo "$entry" > "$temp_file"
cat "$QUEUE_FILE" >> "$temp_file"
mv "$temp_file" "$QUEUE_FILE"
chmod 644 "$QUEUE_FILE"
logger -t at_queue -p daemon.info "Added high priority command to front of queue"
else
# Normal priority - append to queue
echo "$entry" >> "$QUEUE_FILE"
logger -t at_queue -p daemon.info "Added normal priority command to end of queue"
fi
# Release lock
release_lock
echo "{\"command_id\":\"$cmd_id\",\"status\":\"queued\"}"
}
# Get next command from queue
dequeue_command() {
if [ ! -s "$QUEUE_FILE" ]; then
return 1
fi
# Acquire lock
if ! acquire_lock; then
logger -t at_queue -p daemon.error "Failed to acquire lock for dequeuing command"
return 1
fi
local cmd_entry=$(head -n 1 "$QUEUE_FILE")
local temp_file=$(mktemp)
tail -n +2 "$QUEUE_FILE" > "$temp_file"
mv "$temp_file" "$QUEUE_FILE"
chmod 644 "$QUEUE_FILE"
echo "$cmd_entry" > "$ACTIVE_FILE"
chmod 644 "$ACTIVE_FILE"
# Release lock
release_lock
logger -t at_queue -p daemon.debug "Dequeued command: $(echo "$cmd_entry" | jsonfilter -e '@.command')"
echo "$cmd_entry"
}
# Clean and format AT command output
clean_output() {
local output="$1"
# First format AT command responses for readability
output=$(echo "$output" | sed -E '
# Add newline after AT commands
s/(AT\+[A-Z0-9]+[^ ]*) +/\1\n/g
# Add newline before +RESPONSE lines
s/ +(\+[A-Z0-9]+:)/\n\1/g
# Add newline before OK/ERROR
s/ +(OK|ERROR)$/\n\1/g
')
# Then escape the formatted output for JSON
output=$(escape_json "$output")
echo "$output"
}
# Execute AT command with optimized timeout handling
execute_with_timeout() {
local command="$1"
local timeout="$2"
local cmd_id="$3"
local output_file=$(mktemp)
# Start command in background with immediate output
(sms_tool -D at "$command" > "$output_file" 2>&1; echo $? > "$QUEUE_DIR/$cmd_id.exit") &
local pid=$!
# Start execution tracking
start_execution_tracking "$cmd_id" "$pid"
logger -t at_queue -p daemon.debug "Started command execution: $command (PID: $pid)"
# Wait for completion with shorter polling interval
local start_time=$(date +%s)
local elapsed=0
while [ $elapsed -lt "$timeout" ]; do
if [ -f "$QUEUE_DIR/$cmd_id.exit" ]; then
local exit_code=$(cat "$QUEUE_DIR/$cmd_id.exit")
local output=$(cat "$output_file")
# Cleanup
rm -f "$QUEUE_DIR/pid.$cmd_id" "$QUEUE_DIR/$cmd_id.exit" "$output_file" "$QUEUE_DIR/start_time.$cmd_id"
logger -t at_queue -p daemon.debug "Command completed with exit code $exit_code"
echo "$output"
return $exit_code
fi
elapsed=$(($(date +%s) - start_time))
sleep $POLL_INTERVAL
done
# Handle timeout
if [ -f "$QUEUE_DIR/pid.$cmd_id" ]; then
local pid=$(cat "$QUEUE_DIR/pid.$cmd_id")
kill $pid 2>/dev/null
sleep 0.1
# Force kill if still running
if kill -0 $pid 2>/dev/null; then
kill -KILL $pid 2>/dev/null
fi
local partial_output=$(cat "$output_file" 2>/dev/null || echo "")
# Cleanup
rm -f "$QUEUE_DIR/pid.$cmd_id" "$QUEUE_DIR/$cmd_id.exit" "$output_file" "$QUEUE_DIR/start_time.$cmd_id"
logger -t at_queue -p daemon.warn "Command timed out after $timeout seconds"
echo "${partial_output:-Command timed out after $timeout seconds}"
fi
return 124
}
# Execute AT command and handle response
execute_command() {
local cmd_entry="$1"
local cmd_id=$(echo "$cmd_entry" | jsonfilter -e '@.id')
local cmd_text=$(echo "$cmd_entry" | jsonfilter -e '@.command')
local priority=$(echo "$cmd_entry" | jsonfilter -e '@.priority')
local start_time=$(date +%s%3N)
logger -t at_queue -p daemon.info "Executing command $cmd_id: $cmd_text (priority: $priority)"
# Execute command with timeout
local result=$(execute_with_timeout "$cmd_text" $MAX_TIMEOUT "$cmd_id")
local exit_code=$?
local end_time=$(date +%s%3N)
local duration=$((end_time - start_time))
# Determine status and log level
local status="error"
local log_level="error"
if [ $exit_code -eq 124 ]; then
status="timeout"
logger -t at_queue -p daemon.error "Command $cmd_id timed out after ${duration}ms"
elif echo "$result" | grep -q "OK"; then
status="success"
log_level="info"
logger -t at_queue -p daemon.info "Command $cmd_id completed successfully in ${duration}ms"
elif echo "$result" | grep -q "CME ERROR"; then
status="cme_error"
logger -t at_queue -p daemon.error "Command $cmd_id failed with CME ERROR in ${duration}ms"
else
logger -t at_queue -p daemon.error "Command $cmd_id failed with general error in ${duration}ms"
fi
# Clean and escape the output
local clean_result=$(clean_output "$result")
# Create JSON response
local response=$(cat << EOF
{
"command": {
"id": "$cmd_id",
"text": "$(escape_json "$cmd_text")",
"timestamp": "$(date -Iseconds)"
},
"response": {
"status": "$status",
"raw_output": "$clean_result",
"completion_time": "$end_time",
"duration_ms": $duration
}
}
EOF
)
# Acquire lock for writing result
if ! acquire_lock; then
logger -t at_queue -p daemon.error "Failed to acquire lock for writing result"
else
# Save response
printf "%s" "$response" > "$RESULTS_DIR/$cmd_id.json"
chmod 644 "$RESULTS_DIR/$cmd_id.json"
# Clean up active file
rm -f "$ACTIVE_FILE"
# Release lock
release_lock
fi
echo "$response"
}
# Main queue processing function
process_queue() {
init_queue_system
local last_cleanup=$(date +%s)
local last_log=$(date +%s) # Add a timestamp for less frequent logging
# Make sure the lock directory doesn't exist at startup
[ -d "$LOCK_DIR" ] && rmdir "$LOCK_DIR" 2>/dev/null
logger -t at_queue -p daemon.info "Started queue processing daemon"
while true; do
# Quick cleanup check
local current_time=$(date +%s)
if [ $((current_time - last_cleanup)) -ge $CLEANUP_INTERVAL ]; then
cleanup_old_results
last_cleanup=$current_time
fi
# Skip processing if token is granted to someone
if [ -f "$TOKEN_FILE" ]; then
local token_holder=$(cat "$TOKEN_FILE" | jsonfilter -e '@.id')
local token_time=$(cat "$TOKEN_FILE" | jsonfilter -e '@.timestamp')
local current_time=$(date +%s)
# Check for expired token
if [ $((current_time - token_time)) -gt $TOKEN_TIMEOUT ]; then
logger -t at_queue -p daemon.warn "Removing expired token from $token_holder"
rm -f "$TOKEN_FILE"
else
# Log pause status only every 5 seconds to reduce log spam
if [ $((current_time - last_log)) -ge 5 ]; then
logger -t at_queue -p daemon.debug "Queue processing paused, token held by $token_holder"
last_log=$current_time
fi
sleep $POLL_INTERVAL
continue
fi
fi
# Process queue if not empty and no active command
if [ -s "$QUEUE_FILE" ] && [ ! -f "$ACTIVE_FILE" ]; then
local cmd_entry=$(dequeue_command)
if [ -n "$cmd_entry" ]; then
execute_command "$cmd_entry"
fi
fi
sleep $POLL_INTERVAL
done
}
# CGI command handling
if [ "${SCRIPT_NAME}" != "" ]; then
# Output headers
if [ "$HTTP_HEADERS" != "0" ]; then
echo "Content-Type: application/json"
echo ""
fi
# Parse query string for CGI mode
eval $(echo "$QUERY_STRING" | sed 's/&/;/g')
case "$action" in
"enqueue")
if [ -n "$command" ]; then
logger -t at_queue -p daemon.info "CGI: Received enqueue request for command: $command"
enqueue_command "$command" "$priority"
else
logger -t at_queue -p daemon.error "CGI: Empty command received"
echo "{\"error\":\"No command specified\"}"
fi
;;
"status")
if [ -f "$ACTIVE_FILE" ]; then
logger -t at_queue -p daemon.debug "CGI: Status request - queue active"
cat "$ACTIVE_FILE"
else
logger -t at_queue -p daemon.debug "CGI: Status request - queue idle"
echo "{\"status\":\"idle\"}"
fi
;;
"request_token")
if [ -n "$id" ]; then
logger -t at_queue -p daemon.info "Token request from $id (priority: ${priority:-10})"
request_token "$id" "${priority:-10}" "${timeout:-10}"
else
logger -t at_queue -p daemon.error "Token request missing ID"
echo "{\"error\":\"No requestor ID specified\",\"status\":\"denied\"}"
fi
;;
"release_token")
if [ -n "$id" ]; then
logger -t at_queue -p daemon.info "Token release from $id"
release_token "$id"
else
logger -t at_queue -p daemon.error "Token release missing ID"
echo "{\"error\":\"No requestor ID specified\",\"status\":\"denied\"}"
fi
;;
*)
logger -t at_queue -p daemon.error "CGI: Invalid action received: $action"
echo "{\"error\":\"Invalid action\"}"
;;
esac
exit 0
fi
# CLI command handling
if [ "$1" = "enqueue" ] && [ -n "$2" ]; then
enqueue_command "$2" "${3:-10}"
exit $?
fi
# If not run as CGI, start queue processing
if [ "${SCRIPT_NAME}" = "" ] && [ -z "$1" ]; then
process_queue
fi

View File

@@ -0,0 +1,198 @@
#!/bin/sh
# Configuration
LOGDIR="/www/signal_graphs"
MAX_ENTRIES=10
INTERVAL=60
QUEUE_DIR="/tmp/at_queue"
TOKEN_FILE="$QUEUE_DIR/token"
LOCK_FILE="/tmp/signal_metrics.lock"
METRICS_PID_FILE="/tmp/signal_metrics.pid"
MAX_TOKEN_WAIT=5 # seconds to wait for token acquisition
# Ensure required directories exist
mkdir -p "$LOGDIR" "$QUEUE_DIR"
# Check if another instance is running
check_running() {
if [ -f "$METRICS_PID_FILE" ]; then
pid=$(cat "$METRICS_PID_FILE")
if kill -0 "$pid" 2>/dev/null; then
return 0
fi
rm -f "$METRICS_PID_FILE" 2>/dev/null
fi
return 1
}
# Acquire token directly (minimized version)
acquire_token() {
local metrics_id="METRICS_$(date +%s)_$$"
local priority=20 # Lowest priority for metrics
local max_attempts=20
local attempt=0
while [ $attempt -lt $max_attempts ]; do
# Check if token exists
if [ -f "$TOKEN_FILE" ]; then
# Check current token
local current_holder=$(cat "$TOKEN_FILE" | jsonfilter -e '@.id' 2>/dev/null)
local current_priority=$(cat "$TOKEN_FILE" | jsonfilter -e '@.priority' 2>/dev/null)
local timestamp=$(cat "$TOKEN_FILE" | jsonfilter -e '@.timestamp' 2>/dev/null)
local current_time=$(date +%s)
# Check for expired token
if [ $((current_time - timestamp)) -gt 30 ] || [ -z "$current_holder" ]; then
rm -f "$TOKEN_FILE" 2>/dev/null
elif [ $priority -lt $current_priority ]; then
rm -f "$TOKEN_FILE" 2>/dev/null
else
# Wait and try again
sleep 0.5
attempt=$((attempt + 1))
continue
fi
fi
# Try to create token
echo "{\"id\":\"$metrics_id\",\"priority\":$priority,\"timestamp\":$(date +%s)}" > "$TOKEN_FILE" 2>/dev/null
chmod 644 "$TOKEN_FILE" 2>/dev/null
# Verify we got it
local holder=$(cat "$TOKEN_FILE" 2>/dev/null | jsonfilter -e '@.id' 2>/dev/null)
if [ "$holder" = "$metrics_id" ]; then
echo "$metrics_id"
return 0
fi
sleep 0.5
attempt=$((attempt + 1))
done
return 1
}
# Release token directly
release_token() {
local metrics_id="$1"
if [ -f "$TOKEN_FILE" ]; then
local current_holder=$(cat "$TOKEN_FILE" | jsonfilter -e '@.id' 2>/dev/null)
if [ "$current_holder" = "$metrics_id" ]; then
rm -f "$TOKEN_FILE" 2>/dev/null
fi
fi
}
# Execute AT command directly
execute_at_command() {
local CMD="$1"
sms_tool at "$CMD" -t 3 2>/dev/null
}
# Process all metrics commands with a single token
process_all_metrics() {
# Try to get token
local metrics_id=$(acquire_token)
if [ -z "$metrics_id" ]; then
logger -t at_queue -p daemon.warn "Could not acquire token for metrics - will try again later"
return 1
fi
logger -t at_queue -p daemon.info "Processing all metrics with token $metrics_id"
# Execute all metrics commands with the single token
local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
# RSRP
local rsrp_output=$(execute_at_command "AT+QRSRP")
if [ -n "$rsrp_output" ] && echo "$rsrp_output" | grep -q "QRSRP"; then
local logfile="$LOGDIR/rsrp.json"
[ ! -s "$logfile" ] && echo "[]" > "$logfile"
local temp_file="${logfile}.tmp.$$"
jq --arg dt "$timestamp" \
--arg out "$rsrp_output" \
'. + [{"datetime": $dt, "output": $out}] | .[-'"$MAX_ENTRIES"':]' \
"$logfile" > "$temp_file" 2>/dev/null && mv "$temp_file" "$logfile"
chmod 644 "$logfile"
fi
sleep 0.5
# RSRQ
local rsrq_output=$(execute_at_command "AT+QRSRQ")
if [ -n "$rsrq_output" ] && echo "$rsrq_output" | grep -q "QRSRQ"; then
local logfile="$LOGDIR/rsrq.json"
[ ! -s "$logfile" ] && echo "[]" > "$logfile"
local temp_file="${logfile}.tmp.$$"
jq --arg dt "$timestamp" \
--arg out "$rsrq_output" \
'. + [{"datetime": $dt, "output": $out}] | .[-'"$MAX_ENTRIES"':]' \
"$logfile" > "$temp_file" 2>/dev/null && mv "$temp_file" "$logfile"
chmod 644 "$logfile"
fi
sleep 0.5
# SINR
local sinr_output=$(execute_at_command "AT+QSINR")
if [ -n "$sinr_output" ] && echo "$sinr_output" | grep -q "QSINR"; then
local logfile="$LOGDIR/sinr.json"
[ ! -s "$logfile" ] && echo "[]" > "$logfile"
local temp_file="${logfile}.tmp.$$"
jq --arg dt "$timestamp" \
--arg out "$sinr_output" \
'. + [{"datetime": $dt, "output": $out}] | .[-'"$MAX_ENTRIES"':]' \
"$logfile" > "$temp_file" 2>/dev/null && mv "$temp_file" "$logfile"
chmod 644 "$logfile"
fi
sleep 0.5
# Data usage
local usage_output=$(execute_at_command "AT+QGDCNT?;+QGDNRCNT?")
if [ -n "$usage_output" ] && echo "$usage_output" | grep -q "QGDCNT\|QGDNRCNT"; then
local logfile="$LOGDIR/data_usage.json"
[ ! -s "$logfile" ] && echo "[]" > "$logfile"
local temp_file="${logfile}.tmp.$$"
jq --arg dt "$timestamp" \
--arg out "$usage_output" \
'. + [{"datetime": $dt, "output": $out}] | .[-'"$MAX_ENTRIES"':]' \
"$logfile" > "$temp_file" 2>/dev/null && mv "$temp_file" "$logfile"
chmod 644 "$logfile"
fi
# Release token
release_token "$metrics_id"
logger -t at_queue -p daemon.info "Metrics processing completed"
return 0
}
# Main continuous logging function with proper locking
start_continuous_logging() {
# Check if already running
if check_running; then
logger -t at_queue -p daemon.error "Signal metrics logging already running"
exit 1
fi
# Store PID
echo "$$" > "$METRICS_PID_FILE"
chmod 644 "$METRICS_PID_FILE"
sleep 20 # Initial delay to allow system startup
logger -t at_queue -p daemon.info "Starting continuous signal metrics logging (PID: $$)"
trap 'logger -t at_queue -p daemon.info "Stopping signal metrics logging"; rm -f "$METRICS_PID_FILE"; exit 0' INT TERM
while true; do
process_all_metrics
sleep "$INTERVAL"
done
}
# Start the continuous logging
start_continuous_logging

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,544 @@
#!/bin/sh
# QuecWatch Daemon
# Monitors cellular connectivity and performs recovery actions
# Load UCI configuration functions
. /lib/functions.sh
# Configuration
QUEUE_DIR="/tmp/at_queue"
TOKEN_FILE="$QUEUE_DIR/token"
LOG_DIR="/tmp/log/quecwatch"
LOG_FILE="$LOG_DIR/quecwatch.log"
PID_FILE="/var/run/quecwatch.pid"
STATUS_FILE="/tmp/quecwatch_status.json"
RETRY_COUNT_FILE="/tmp/quecwatch_retry_count"
UCI_CONFIG="quecmanager"
MAX_TOKEN_WAIT=10 # Maximum seconds to wait for token acquisition
TOKEN_PRIORITY=15 # Medium priority (between profiles and metrics)
# Ensure directories exist
mkdir -p "$LOG_DIR" "$QUEUE_DIR"
# Store PID
echo "$$" > "$PID_FILE"
chmod 644 "$PID_FILE"
# Function to log messages
log_message() {
local level="${2:-info}"
local message="$1"
local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
# Log to file
echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
# Log to system log
logger -t quecwatch -p "daemon.$level" "$message"
}
# Function to update status
update_status() {
local status="$1"
local message="$2"
local retry="${3:-$CURRENT_RETRIES}"
local max="${4:-$MAX_RETRIES}"
# Create JSON status
cat > "$STATUS_FILE" <<EOF
{
"status": "$status",
"message": "$message",
"retry": $retry,
"maxRetries": $max,
"timestamp": $(date +%s)
}
EOF
chmod 644 "$STATUS_FILE"
log_message "Status updated: $status - $message" "debug"
}
# Function to acquire token for AT commands
acquire_token() {
local requestor_id="QUECWATCH_$(date +%s)_$$"
local priority="$TOKEN_PRIORITY"
local max_attempts=$MAX_TOKEN_WAIT
local attempt=0
log_message "Attempting to acquire token with priority $priority" "debug"
while [ $attempt -lt $max_attempts ]; do
# Check if token file exists
if [ -f "$TOKEN_FILE" ]; then
local current_holder=$(cat "$TOKEN_FILE" | jsonfilter -e '@.id' 2>/dev/null)
local current_priority=$(cat "$TOKEN_FILE" | jsonfilter -e '@.priority' 2>/dev/null)
local timestamp=$(cat "$TOKEN_FILE" | jsonfilter -e '@.timestamp' 2>/dev/null)
local current_time=$(date +%s)
# Check for expired token (> 30 seconds old)
if [ $((current_time - timestamp)) -gt 30 ] || [ -z "$current_holder" ]; then
# Remove expired token
log_message "Found expired token from $current_holder, removing" "debug"
rm -f "$TOKEN_FILE" 2>/dev/null
elif [ $priority -lt $current_priority ]; then
# Preempt lower priority token
log_message "Preempting token from $current_holder (priority: $current_priority)" "debug"
rm -f "$TOKEN_FILE" 2>/dev/null
else
# Check if the token is held by a QuecProfile or cell scan
if echo "$current_holder" | grep -q "CELL_SCAN"; then
log_message "Token held by cell scan (priority: $current_priority), waiting..." "debug"
elif echo "$current_holder" | grep -q "QUECPROFILES"; then
log_message "Token held by profile application (priority: $current_priority), waiting..." "debug"
else
log_message "Token held by $current_holder with priority $current_priority, retrying..." "debug"
fi
sleep 0.5
attempt=$((attempt + 1))
continue
fi
fi
# Try to create token file
echo "{\"id\":\"$requestor_id\",\"priority\":$priority,\"timestamp\":$(date +%s)}" > "$TOKEN_FILE" 2>/dev/null
chmod 644 "$TOKEN_FILE" 2>/dev/null
# Verify we got the token
local holder=$(cat "$TOKEN_FILE" 2>/dev/null | jsonfilter -e '@.id' 2>/dev/null)
if [ "$holder" = "$requestor_id" ]; then
log_message "Successfully acquired token with ID $requestor_id" "debug"
echo "$requestor_id"
return 0
fi
sleep 0.5
attempt=$((attempt + 1))
done
log_message "Failed to acquire token after $max_attempts attempts" "error"
return 1
}
# Function to release token
release_token() {
local requestor_id="$1"
if [ -f "$TOKEN_FILE" ]; then
local current_holder=$(cat "$TOKEN_FILE" | jsonfilter -e '@.id' 2>/dev/null)
if [ "$current_holder" = "$requestor_id" ]; then
rm -f "$TOKEN_FILE" 2>/dev/null
log_message "Released token $requestor_id" "debug"
return 0
fi
log_message "Token held by $current_holder, not by us ($requestor_id)" "warn"
else
log_message "Token file doesn't exist, nothing to release" "debug"
fi
return 1
}
# Function to execute AT command with token
execute_at_command() {
local cmd="$1"
local timeout="${2:-5}"
local token_id="$3"
if [ -z "$token_id" ]; then
log_message "No valid token provided for command: $cmd" "error"
return 1
fi
log_message "Executing AT command: $cmd (timeout: ${timeout}s)" "debug"
# Execute the command with proper timeout
local output
local status=1
output=$(sms_tool at "$cmd" -t "$timeout" 2>&1)
status=$?
if [ $status -ne 0 ]; then
log_message "AT command failed: $cmd (exit code: $status)" "error"
return 1
fi
echo "$output"
return 0
}
# Function to check internet connectivity
check_internet() {
local ping_target
local ping_count=3
# Get ping target from UCI
config_load "$UCI_CONFIG"
config_get ping_target quecwatch ping_target
if [ -z "$ping_target" ]; then
log_message "No ping target configured" "error"
return 1
fi
log_message "Checking internet connectivity to $ping_target" "debug"
if ping -c $ping_count "$ping_target" > /dev/null 2>&1; then
log_message "Internet connectivity check successful" "debug"
return 0
else
log_message "Internet connectivity check failed" "warn"
return 1
fi
}
# Function to get current SIM slot
get_current_sim() {
local token_id=$(acquire_token)
if [ -z "$token_id" ]; then
log_message "Failed to acquire token for SIM slot check" "error"
return 1
fi
log_message "Checking current SIM slot" "debug"
local result=$(execute_at_command "AT+QUIMSLOT?" 5 "$token_id")
local status=$?
# Release token
release_token "$token_id"
if [ $status -eq 0 ] && [ -n "$result" ]; then
# Extract SIM slot number from response
local current_sim=$(echo "$result" | grep -o '+QUIMSLOT: [0-9]' | cut -d' ' -f2)
if [ -n "$current_sim" ]; then
log_message "Current SIM slot: $current_sim" "debug"
echo "$current_sim"
return 0
fi
fi
log_message "Failed to get current SIM slot" "error"
return 1
}
# Function to switch SIM card
switch_sim_card() {
local current_sim
local target_sim
local token_id
log_message "Starting SIM card switch operation" "info"
# Get current SIM slot
current_sim=$(get_current_sim)
if [ $? -ne 0 ]; then
log_message "Failed to get current SIM slot, cannot switch" "error"
return 1
fi
# Determine target SIM
if [ "$current_sim" = "1" ]; then
target_sim=2
else
target_sim=1
fi
log_message "Attempting to switch from SIM $current_sim to SIM $target_sim" "info"
# Get token for AT commands
token_id=$(acquire_token)
if [ -z "$token_id" ]; then
log_message "Failed to acquire token for SIM switch" "error"
return 1
fi
# Detach from network
log_message "Detaching from network" "debug"
execute_at_command "AT+COPS=2" 10 "$token_id"
sleep 2
# Switch SIM slot
log_message "Switching to SIM slot $target_sim" "debug"
local switch_result=$(execute_at_command "AT+QUIMSLOT=$target_sim" 10 "$token_id")
local switch_status=$?
# If switch failed, return error
if [ $switch_status -ne 0 ]; then
log_message "Failed to switch to SIM $target_sim" "error"
release_token "$token_id"
return 1
fi
sleep 5
# Reattach to network
log_message "Reattaching to network" "debug"
execute_at_command "AT+COPS=0" 10 "$token_id"
# Release token
release_token "$token_id"
# Verify switch
sleep 10
local new_sim=$(get_current_sim)
if [ "$new_sim" = "$target_sim" ]; then
log_message "Successfully switched to SIM $target_sim" "info"
return 0
else
log_message "Failed to verify SIM switch, current SIM is $new_sim" "error"
return 1
fi
}
# Function to perform connection recovery
perform_connection_recovery() {
local token_id
log_message "Starting connection recovery" "info"
# Get token for AT commands
token_id=$(acquire_token)
if [ -z "$token_id" ]; then
log_message "Failed to acquire token for connection recovery" "error"
return 1
fi
# Detach from network
log_message "Detaching from network" "debug"
execute_at_command "AT+COPS=2" 10 "$token_id"
sleep 2
# Reattach to network
log_message "Reattaching to network" "debug"
execute_at_command "AT+COPS=0" 15 "$token_id"
# Release token
release_token "$token_id"
# Verify recovery
sleep 10
if check_internet; then
log_message "Connection recovery successful" "info"
return 0
else
log_message "Connection recovery failed" "error"
return 1
fi
}
# Load configuration
load_config() {
# Initialize variables
PING_TARGET=""
PING_INTERVAL=60
PING_FAILURES=3
MAX_RETRIES=5
CURRENT_RETRIES=0
CONNECTION_REFRESH=0
REFRESH_COUNT=3
AUTO_SIM_FAILOVER=0
SIM_FAILOVER_SCHEDULE=0
# Load from UCI
config_load "$UCI_CONFIG"
# Get settings with defaults
config_get PING_TARGET quecwatch ping_target
config_get PING_INTERVAL quecwatch ping_interval 60
config_get PING_FAILURES quecwatch ping_failures 3
config_get MAX_RETRIES quecwatch max_retries 5
config_get CURRENT_RETRIES quecwatch current_retries 0
config_get_bool CONNECTION_REFRESH quecwatch connection_refresh 0
config_get REFRESH_COUNT quecwatch refresh_count 3
config_get_bool AUTO_SIM_FAILOVER quecwatch auto_sim_failover 0
config_get SIM_FAILOVER_SCHEDULE quecwatch sim_failover_schedule 0
# Validate required settings
if [ -z "$PING_TARGET" ]; then
log_message "No ping target configured, using default (8.8.8.8)" "warn"
PING_TARGET="8.8.8.8"
uci set "$UCI_CONFIG.quecwatch.ping_target=$PING_TARGET"
uci commit "$UCI_CONFIG"
fi
# Load persisted retry count if available
if [ -f "$RETRY_COUNT_FILE" ]; then
CURRENT_RETRIES=$(cat "$RETRY_COUNT_FILE")
fi
log_message "Configuration loaded: ping_target=$PING_TARGET, interval=$PING_INTERVAL, failures=$PING_FAILURES, max_retries=$MAX_RETRIES, current_retries=$CURRENT_RETRIES" "info"
}
# Save retry count to both UCI and file
save_retry_count() {
local count=$1
# Update UCI
uci set "$UCI_CONFIG.quecwatch.current_retries=$count"
uci commit "$UCI_CONFIG"
# Update file for crash recovery
echo "$count" > "$RETRY_COUNT_FILE"
chmod 644 "$RETRY_COUNT_FILE"
log_message "Updated retry count to $count" "debug"
}
# Main monitoring function
main() {
log_message "QuecWatch daemon starting (PID: $$)" "info"
# Load configuration
load_config
# Initial status update
update_status "active" "Monitoring started"
# Track consecutive failures
local failure_count=0
# For scheduled SIM failover
local sim_failover_interval=0
local initial_sim=""
# If auto SIM failover is enabled, store initial SIM slot
if [ "$AUTO_SIM_FAILOVER" -eq 1 ]; then
initial_sim=$(get_current_sim)
if [ -n "$initial_sim" ]; then
log_message "Auto SIM failover enabled, initial SIM slot: $initial_sim" "info"
fi
fi
# Main monitoring loop
while true; do
log_message "Starting monitoring cycle" "debug"
# Check internet connectivity
if ! check_internet; then
failure_count=$((failure_count + 1))
log_message "Connectivity check failed ($failure_count/$PING_FAILURES)" "warn"
# Update status
update_status "warning" "Connection check failed: $failure_count/$PING_FAILURES failures"
# Check if failure threshold is reached
if [ $failure_count -ge $PING_FAILURES ]; then
# Reset failure counter
failure_count=0
# Increment retry counter
CURRENT_RETRIES=$((CURRENT_RETRIES + 1))
save_retry_count $CURRENT_RETRIES
log_message "Failure threshold reached. Current retry: $CURRENT_RETRIES/$MAX_RETRIES" "warn"
update_status "error" "Connection lost, attempt $CURRENT_RETRIES/$MAX_RETRIES to recover"
# Check if max retries reached
if [ $CURRENT_RETRIES -ge $MAX_RETRIES ]; then
log_message "Maximum retries reached" "error"
# Try SIM failover if enabled
if [ "$AUTO_SIM_FAILOVER" -eq 1 ]; then
log_message "Attempting SIM failover" "info"
update_status "failover" "Maximum retries reached, attempting SIM failover"
if switch_sim_card && check_internet; then
log_message "SIM failover successful, connection restored" "info"
update_status "recovered" "Connection restored via SIM failover"
# Reset retry counter
CURRENT_RETRIES=0
save_retry_count $CURRENT_RETRIES
else
log_message "SIM failover failed, system will reboot" "error"
update_status "rebooting" "SIM failover failed, system will reboot"
# Wait briefly and reboot
sleep 5
reboot
fi
else
log_message "Auto SIM failover disabled, system will reboot" "error"
update_status "rebooting" "Maximum retries reached, system will reboot"
# Wait briefly and reboot
sleep 5
reboot
fi
else
# Try connection recovery
log_message "Attempting connection recovery" "info"
update_status "recovering" "Attempting to restore connection"
if perform_connection_recovery; then
log_message "Connection recovery successful" "info"
update_status "recovered" "Connection restored"
# Reset retry counter
CURRENT_RETRIES=0
save_retry_count $CURRENT_RETRIES
fi
fi
fi
else
# Connection is good
if [ $failure_count -gt 0 ] || [ $CURRENT_RETRIES -gt 0 ]; then
log_message "Connection restored" "info"
update_status "stable" "Connection restored"
# Reset counters
failure_count=0
CURRENT_RETRIES=0
save_retry_count $CURRENT_RETRIES
fi
# Scheduled SIM failover check
if [ "$AUTO_SIM_FAILOVER" -eq 1 ] && [ "$SIM_FAILOVER_SCHEDULE" -gt 0 ] && [ -n "$initial_sim" ]; then
# Get current SIM to check if we're on the backup
local current_sim=$(get_current_sim)
# If we're on backup SIM, check if it's time to try primary again
if [ -n "$current_sim" ] && [ "$current_sim" != "$initial_sim" ]; then
sim_failover_interval=$((sim_failover_interval + 1))
# Check if we've reached the scheduled time
if [ $((sim_failover_interval * PING_INTERVAL)) -ge $((SIM_FAILOVER_SCHEDULE * 60)) ]; then
log_message "Scheduled check: attempting to switch back to primary SIM $initial_sim" "info"
update_status "switchback" "Attempting to switch back to primary SIM"
# Try switching back
if switch_sim_card && check_internet; then
log_message "Successfully switched back to primary SIM" "info"
update_status "stable" "Successfully switched back to primary SIM"
else
log_message "Failed to switch back to primary SIM, staying on backup" "warn"
update_status "stable" "Staying on backup SIM - primary SIM check failed"
# Switch back to backup SIM
current_sim=$(get_current_sim)
if [ -n "$current_sim" ] && [ "$current_sim" = "$initial_sim" ]; then
switch_sim_card
fi
fi
# Reset failover interval
sim_failover_interval=0
fi
fi
fi
fi
# Sleep for the configured interval
sleep $PING_INTERVAL
done
}
# Set up trap for clean shutdown
trap 'log_message "Received signal, exiting" "info"; update_status "stopped" "Daemon stopped"; rm -f "$PID_FILE"; exit 0' INT TERM
# Start the main function
main

View File

@@ -0,0 +1,429 @@
#!/bin/sh
# Cell Lock Scheduler Daemon
# Monitors schedule and applies/restores cell locks as needed
# Load UCI configuration functions
. /lib/functions.sh
# Configuration
QUEUE_DIR="/tmp/at_queue"
TOKEN_FILE="$QUEUE_DIR/token"
LOG_DIR="/tmp/log/cell_lock"
LOG_FILE="$LOG_DIR/cell_lock.log"
PID_FILE="/var/run/cell_lock_scheduler.pid"
STATUS_FILE="/tmp/cell_lock_status.json"
UCI_CONFIG="quecmanager"
CHECK_INTERVAL=60 # Check schedule every minute
MAX_TOKEN_WAIT=15 # Maximum seconds to wait for token acquisition
TOKEN_PRIORITY=5 # Higher priority than QuecWatch (which is 15)
# Ensure directories exist
mkdir -p "$LOG_DIR" "$QUEUE_DIR"
# Store PID
echo "$$" > "$PID_FILE"
chmod 644 "$PID_FILE"
# Function to log messages
log_message() {
local level="${2:-info}"
local message="$1"
local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
# Log to file
echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
# Log to system log
logger -t cell_lock -p "daemon.$level" "$message"
}
# Function to update status
update_status() {
local status="$1"
local message="$2"
local active="${3:-0}"
local locked="${4:-0}"
# Create JSON status
cat > "$STATUS_FILE" <<EOF
{
"status": "$status",
"message": "$message",
"active": $active,
"locked": $locked,
"timestamp": $(date +%s)
}
EOF
chmod 644 "$STATUS_FILE"
log_message "Status updated: $status - $message" "debug"
}
# Function to acquire token for AT commands
acquire_token() {
local requestor_id="CELLLOCK_$(date +%s)_$$"
local priority="$TOKEN_PRIORITY"
local max_attempts=$MAX_TOKEN_WAIT
local attempt=0
log_message "Attempting to acquire token with priority $priority" "debug"
while [ $attempt -lt $max_attempts ]; do
# Check if token file exists
if [ -f "$TOKEN_FILE" ]; then
local current_holder=$(cat "$TOKEN_FILE" | jsonfilter -e '@.id' 2>/dev/null)
local current_priority=$(cat "$TOKEN_FILE" | jsonfilter -e '@.priority' 2>/dev/null)
local timestamp=$(cat "$TOKEN_FILE" | jsonfilter -e '@.timestamp' 2>/dev/null)
local current_time=$(date +%s)
# Check for expired token (> 30 seconds old)
if [ $((current_time - timestamp)) -gt 30 ] || [ -z "$current_holder" ]; then
# Remove expired token
log_message "Found expired token from $current_holder, removing" "debug"
rm -f "$TOKEN_FILE" 2>/dev/null
elif [ $priority -lt $current_priority ]; then
# Preempt lower priority token
log_message "Preempting token from $current_holder (priority: $current_priority)" "debug"
rm -f "$TOKEN_FILE" 2>/dev/null
else
# Check if the token is held by a cell scan
if echo "$current_holder" | grep -q "CELL_SCAN"; then
log_message "Token held by cell scan (priority: $current_priority), waiting..." "debug"
else
log_message "Token held by $current_holder with priority $current_priority, retrying..." "debug"
fi
sleep 0.5
attempt=$((attempt + 1))
continue
fi
fi
# Try to create token file
echo "{\"id\":\"$requestor_id\",\"priority\":$priority,\"timestamp\":$(date +%s)}" > "$TOKEN_FILE" 2>/dev/null
chmod 644 "$TOKEN_FILE" 2>/dev/null
# Verify we got the token
local holder=$(cat "$TOKEN_FILE" 2>/dev/null | jsonfilter -e '@.id' 2>/dev/null)
if [ "$holder" = "$requestor_id" ]; then
log_message "Successfully acquired token with ID $requestor_id" "debug"
echo "$requestor_id"
return 0
fi
sleep 0.5
attempt=$((attempt + 1))
done
log_message "Failed to acquire token after $max_attempts attempts" "error"
return 1
}
# Function to release token
release_token() {
local requestor_id="$1"
if [ -f "$TOKEN_FILE" ]; then
local current_holder=$(cat "$TOKEN_FILE" | jsonfilter -e '@.id' 2>/dev/null)
if [ "$current_holder" = "$requestor_id" ]; then
rm -f "$TOKEN_FILE" 2>/dev/null
log_message "Released token $requestor_id" "debug"
return 0
fi
log_message "Token held by $current_holder, not by us ($requestor_id)" "warn"
else
log_message "Token file doesn't exist, nothing to release" "debug"
fi
return 1
}
# Function to execute AT command with token
execute_at_command() {
local cmd="$1"
local timeout="${2:-10}"
local token_id="$3"
if [ -z "$token_id" ]; then
log_message "No valid token provided for command: $cmd" "error"
return 1
fi
log_message "Executing AT command: $cmd (timeout: ${timeout}s)" "debug"
# Execute the command with proper timeout
local output
local status=1
output=$(sms_tool at "$cmd" -t "$timeout" 2>&1)
status=$?
if [ $status -ne 0 ]; then
log_message "AT command failed: $cmd (exit code: $status)" "error"
return 1
fi
echo "$output"
return 0
}
# Function to check current lock status
check_lock_status() {
local token_id="$1"
log_message "Checking current cell lock status" "debug"
# Check LTE lock status
local lte_status=$(execute_at_command 'AT+QNWLOCK="common/4g"' 5 "$token_id")
local nr5g_status=$(execute_at_command 'AT+QNWLOCK="common/5g"' 5 "$token_id")
# Check if any lock is active
if echo "$lte_status" | grep -q '"common/4g",0'; then
if echo "$nr5g_status" | grep -q '"common/5g",0'; then
log_message "No active cell locks detected" "debug"
return 1
fi
fi
log_message "Active cell locks detected" "debug"
return 0
}
# Function to get current lock parameters and save to UCI
store_current_lock_params() {
local token_id="$1"
log_message "Storing current lock parameters" "debug"
# Get LTE lock status
local lte_status=$(execute_at_command 'AT+QNWLOCK="common/4g"' 5 "$token_id")
if [ $? -eq 0 ]; then
# Extract parameters
local lte_params=$(echo "$lte_status" | grep -o '"common/4g",[^[:space:]]*' | cut -d',' -f2-)
# Save to UCI
uci set "$UCI_CONFIG.cell_lock.lte_params='$lte_params'"
log_message "Stored LTE parameters: $lte_params" "debug"
fi
# Get NR5G lock status
local nr5g_status=$(execute_at_command 'AT+QNWLOCK="common/5g"' 5 "$token_id")
if [ $? -eq 0 ]; then
# Extract parameters
local nr5g_params=$(echo "$nr5g_status" | grep -o '"common/5g",[^[:space:]]*' | cut -d',' -f2-)
# Save to UCI
uci set "$UCI_CONFIG.cell_lock.nr5g_params='$nr5g_params'"
log_message "Stored NR5G parameters: $nr5g_params" "debug"
fi
# Get persist settings
local persist_status=$(execute_at_command 'AT+QNWLOCK="save_ctrl"' 5 "$token_id")
if [ $? -eq 0 ]; then
# Extract parameters (LTE persist is at index 1, NR5G persist is at index 2)
local persist_params=$(echo "$persist_status" | grep -o '"save_ctrl",[^[:space:]]*' | cut -d',' -f2-)
local lte_persist=$(echo "$persist_params" | cut -d',' -f1)
local nr5g_persist=$(echo "$persist_params" | cut -d',' -f2)
# Save to UCI
uci set "$UCI_CONFIG.cell_lock.lte_persist='$lte_persist'"
uci set "$UCI_CONFIG.cell_lock.nr5g_persist='$nr5g_persist'"
log_message "Stored persist settings: LTE=$lte_persist, NR5G=$nr5g_persist" "debug"
fi
# Commit changes
uci commit "$UCI_CONFIG"
return 0
}
# Function to check if time is in range
is_time_in_range() {
local current_time_minutes=$1
local start_time_minutes=$2
local end_time_minutes=$3
# Handle case where end time is on the next day
if [ $end_time_minutes -lt $start_time_minutes ]; then
if [ $current_time_minutes -ge $start_time_minutes ] || [ $current_time_minutes -lt $end_time_minutes ]; then
return 0
fi
else
if [ $current_time_minutes -ge $start_time_minutes ] && [ $current_time_minutes -lt $end_time_minutes ]; then
return 0
fi
fi
return 1
}
# Function to convert HH:MM to minutes
time_to_minutes() {
local time="$1"
local hours=$(echo "$time" | cut -d':' -f1)
local minutes=$(echo "$time" | cut -d':' -f2)
echo $((hours * 60 + minutes))
}
# Function to check schedule and manage cell locks
check_schedule() {
local enabled
local start_time
local end_time
local current_active
# Get current scheduler state from UCI
config_load "$UCI_CONFIG"
config_get_bool enabled cell_lock enabled 0
if [ "$enabled" -ne 1 ]; then
log_message "Cell lock scheduler is disabled" "debug"
update_status "disabled" "Scheduler is disabled" 0 0
return 0
fi
# Get schedule from UCI
config_get start_time cell_lock start_time
config_get end_time cell_lock end_time
config_get current_active cell_lock active 0
if [ -z "$start_time" ] || [ -z "$end_time" ]; then
log_message "Missing start or end time in configuration" "error"
update_status "error" "Missing schedule configuration" 0 0
return 1
}
# Get current time
local current_time=$(date "+%H:%M")
# Convert times to minutes for comparison
local current_minutes=$(time_to_minutes "$current_time")
local start_minutes=$(time_to_minutes "$start_time")
local end_minutes=$(time_to_minutes "$end_time")
# Get token for AT commands
local token_id=$(acquire_token)
if [ -z "$token_id" ]; then
log_message "Failed to acquire token for checking schedule" "error"
update_status "error" "Failed to acquire token for checking schedule" 0 0
return 1
}
# Check if any cell lock is currently active
local lock_active=0
check_lock_status "$token_id" && lock_active=1
# Check if current time is in the scheduled range
if is_time_in_range "$current_minutes" "$start_minutes" "$end_minutes"; then
# We're in the active window
if [ "$current_active" -ne 1 ]; then
# We just entered the window, need to save current state
log_message "Entering scheduled window" "info"
# Store current lock parameters if a lock is active
if [ $lock_active -eq 1 ]; then
log_message "Storing current cell lock parameters" "info"
store_current_lock_params "$token_id"
else
log_message "No active cell locks to store" "info"
update_status "inactive" "Schedule active but no cell locks configured" 1 0
release_token "$token_id"
return 0
}
# Update status
uci set "$UCI_CONFIG.cell_lock.active=1"
uci commit "$UCI_CONFIG"
update_status "active" "Cell lock scheduler is active" 1 $lock_active
else
update_status "active" "Cell lock scheduler is active" 1 $lock_active
}
else
# We're outside the active window
if [ "$current_active" -eq 1 ]; then
# We just exited the window
log_message "Exiting scheduled window" "info"
# Update status
uci set "$UCI_CONFIG.cell_lock.active=0"
uci commit "$UCI_CONFIG"
update_status "inactive" "Outside scheduled hours" 0 $lock_active
} else {
update_status "inactive" "Outside scheduled hours" 0 $lock_active
}
fi
# Release token
release_token "$token_id"
return 0
}
# Main function
main() {
log_message "Cell lock scheduler daemon starting (PID: $$)" "info"
# Ensure UCI section exists
if ! uci -q get "$UCI_CONFIG.cell_lock" >/dev/null; then
uci set "$UCI_CONFIG.cell_lock=scheduler"
uci set "$UCI_CONFIG.cell_lock.enabled=0"
uci set "$UCI_CONFIG.cell_lock.active=0"
uci commit "$UCI_CONFIG"
log_message "Created cell lock UCI configuration" "info"
fi
# Initialize status
update_status "starting" "Cell lock scheduler daemon starting" 0 0
# Get token and check if any locks are active
local token_id=$(acquire_token)
if [ -n "$token_id" ]; then
local lock_active=0
check_lock_status "$token_id" && lock_active=1
release_token "$token_id"
# Update status based on current state
local enabled=$(uci -q get "$UCI_CONFIG.cell_lock.enabled")
if [ "$enabled" = "1" ]; then
# Get schedule from UCI
local start_time=$(uci -q get "$UCI_CONFIG.cell_lock.start_time")
local end_time=$(uci -q get "$UCI_CONFIG.cell_lock.end_time")
if [ -n "$start_time" ] && [ -n "$end_time" ]; then
# Check if we're currently in the schedule window
local current_time=$(date "+%H:%M")
local current_minutes=$(time_to_minutes "$current_time")
local start_minutes=$(time_to_minutes "$start_time")
local end_minutes=$(time_to_minutes "$end_time")
if is_time_in_range "$current_minutes" "$start_minutes" "$end_minutes"; then
update_status "active" "Cell lock scheduler is active" 1 $lock_active
uci set "$UCI_CONFIG.cell_lock.active=1"
uci commit "$UCI_CONFIG"
} else {
update_status "inactive" "Cell lock scheduler is enabled but outside scheduled hours" 0 $lock_active
uci set "$UCI_CONFIG.cell_lock.active=0"
uci commit "$UCI_CONFIG"
}
} else {
update_status "error" "Missing schedule configuration" 0 $lock_active
}
} else {
update_status "disabled" "Cell lock scheduler is disabled" 0 $lock_active
}
} else {
log_message "Failed to acquire token for initial status check" "error"
}
# Main monitoring loop
while true; do
check_schedule
sleep $CHECK_INTERVAL
done
}
# Set up trap for clean shutdown
trap 'log_message "Received signal, exiting" "info"; update_status "stopped" "Daemon stopped" 0 0; rm -f "$PID_FILE"; exit 0' INT TERM
# Start the main function
main