Added missing cgi-bin directory
This commit is contained in:
@@ -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
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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
|
||||
@@ -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"}'
|
||||
@@ -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"}'
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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}'
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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
@@ -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
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user