QuecManager non-beta

Its about time I did this!
This commit is contained in:
Cameron Thompson
2025-04-02 23:09:08 -04:00
parent c4a340bd36
commit c42907e346
482 changed files with 47267 additions and 109914 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,155 @@
#!/bin/sh
# Cell Lock Apply Script - Called by crontab at start time
# Configuration
UCI_CONFIG="quecmanager"
LOG_DIR="/tmp/log/cell_lock"
LOG_FILE="$LOG_DIR/cell_lock.log"
STATUS_FILE="/tmp/cell_lock_status.json"
QUEUE_DIR="/tmp/at_queue"
TOKEN_FILE="$QUEUE_DIR/token"
MAX_TOKEN_WAIT=15
TOKEN_PRIORITY=5
# Ensure log directory exists
mkdir -p "$LOG_DIR"
# Function to log messages
log_message() {
local message="$1"
local level="${2:-info}"
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_apply -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" <<EOF2
{
"status": "$status",
"message": "$message",
"active": $active,
"locked": $locked,
"timestamp": $(date +%s)
}
EOF2
chmod 644 "$STATUS_FILE"
log_message "Status updated: $status - $message (active=$active, locked=$locked)"
}
# Function to execute AT command
execute_at_command() {
local cmd="$1"
local timeout="${2:-10}"
# 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
}
# Main function to apply cell lock
apply_cell_lock() {
log_message "Applying cell lock at scheduled start time"
# Mark as active in UCI
uci set "$UCI_CONFIG.cell_lock.active=1"
uci commit "$UCI_CONFIG"
# Update status
update_status "active" "Cell lock scheduler is active - scheduled start time reached" 1 1
# Get lock parameters from UCI
local lte_lock_params=$(uci -q get "$UCI_CONFIG.cell_lock.lte_lock")
local nr5g_lock_params=$(uci -q get "$UCI_CONFIG.cell_lock.nr5g_lock")
log_message "Lock parameters from UCI: LTE=$lte_lock_params, NR5G=$nr5g_lock_params"
# Apply locks if parameters exist
local success=0
if [ -n "$lte_lock_params" ]; then
log_message "Applying LTE lock: $lte_lock_params"
local lte_cmd="AT+QNWLOCK=\"common/4g\",$lte_lock_params"
execute_at_command "$lte_cmd" 10
if [ $? -eq 0 ]; then
log_message "LTE lock applied successfully"
success=1
else
log_message "Failed to apply LTE lock" "error"
fi
else
log_message "No LTE lock parameters found, checking for current lock"
# If no parameters set, try to lock to current serving cell
local scan_output=$(execute_at_command "AT+QENG=\"servingcell\"" 5)
log_message "Current serving cell info: $scan_output"
# Parse and apply if possible (simplified for this example)
fi
if [ -n "$nr5g_lock_params" ]; then
log_message "Applying NR5G lock: $nr5g_lock_params"
local nr5g_cmd="AT+QNWLOCK=\"common/5g\",$nr5g_lock_params"
execute_at_command "$nr5g_cmd" 10
if [ $? -eq 0 ]; then
log_message "NR5G lock applied successfully"
success=1
else
log_message "Failed to apply NR5G lock" "error"
fi
fi
# Apply persist settings
local lte_persist=$(uci -q get "$UCI_CONFIG.cell_lock.lte_persist")
local nr5g_persist=$(uci -q get "$UCI_CONFIG.cell_lock.nr5g_persist")
# Default to 0 if not set
lte_persist="${lte_persist:-0}"
nr5g_persist="${nr5g_persist:-0}"
local persist_cmd="AT+QNWLOCK=\"save_ctrl\",$lte_persist,$nr5g_persist"
execute_at_command "$persist_cmd" 10
# Reset network to apply changes
log_message "Resetting network connection to apply changes"
execute_at_command "AT+COPS=2" 5
sleep 2
execute_at_command "AT+COPS=0" 5
if [ $success -eq 1 ]; then
log_message "Cell lock applied at scheduled start time"
update_status "active" "Cell lock applied at scheduled start time" 1 1
else
log_message "Failed to apply cell lock" "error"
update_status "error" "Failed to apply cell lock" 1 0
fi
return 0
}
# Execute main function
log_message "====== STARTING SCHEDULED CELL LOCK APPLICATION ======" "notice"
apply_cell_lock
log_message "====== COMPLETED SCHEDULED CELL LOCK APPLICATION ======" "notice"

View File

@@ -0,0 +1,359 @@
#!/bin/sh
# Boot-time Cell Lock Checker - Called from init.d script at boot
# Configuration
UCI_CONFIG="quecmanager"
LOG_DIR="/tmp/log/cell_lock"
LOG_FILE="$LOG_DIR/cell_lock.log"
STATUS_FILE="/tmp/cell_lock_status.json"
QUEUE_DIR="/tmp/at_queue"
TOKEN_FILE="$QUEUE_DIR/token"
MAX_TOKEN_WAIT=15
TOKEN_PRIORITY=5
ROTATE_SIZE=500 # KB before log rotation
# Ensure log directory exists
mkdir -p "$LOG_DIR"
# Enhanced log_message function
log_message() {
local message="$1"
local level="${2:-info}"
local component="boot_check"
local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
local pid=$$
# Check if log file is too large (>500KB) and rotate if needed
if [ -f "$LOG_FILE" ] && [ $(du -k "$LOG_FILE" | cut -f1) -gt $ROTATE_SIZE ]; then
mv "$LOG_FILE" "$LOG_FILE.old"
touch "$LOG_FILE"
chmod 644 "$LOG_FILE"
fi
# Format: [timestamp] [level] [component] [pid] message
echo "[$timestamp] [$level] [$component] [$pid] $message" >>"$LOG_FILE"
# Also log to system log with appropriate priority
case "$level" in
debug) logger -t "cell_lock_$component" -p daemon.debug "$message" ;;
info) logger -t "cell_lock_$component" -p daemon.info "$message" ;;
notice) logger -t "cell_lock_$component" -p daemon.notice "$message" ;;
warn) logger -t "cell_lock_$component" -p daemon.warning "$message" ;;
error) logger -t "cell_lock_$component" -p daemon.err "$message" ;;
crit) logger -t "cell_lock_$component" -p daemon.crit "$message" ;;
*) logger -t "cell_lock_$component" -p daemon.info "$message" ;;
esac
}
# 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" <<EOF2
{
"status": "$status",
"message": "$message",
"active": $active,
"locked": $locked,
"timestamp": $(date +%s)
}
EOF2
chmod 644 "$STATUS_FILE"
log_message "Status updated: $status - $message (active=$active, locked=$locked)" "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
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\":\"$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
log_message "AT command executed successfully: $cmd" "debug"
echo "$output"
return 0
}
# Function to convert HH:MM to minutes with better error handling
time_to_minutes() {
local time="$1"
# Check if time is empty or malformed
if [ -z "$time" ] || ! echo "$time" | grep -q '^[0-9]\{1,2\}:[0-9]\{2\}$'; then
log_message "Invalid time format: '$time'" "error"
echo "0"
return 1
fi
local hours=$(echo "$time" | cut -d':' -f1)
local minutes=$(echo "$time" | cut -d':' -f2)
# Remove leading zeros which can cause issues in arithmetic
hours=$(echo "$hours" | sed 's/^0*//')
minutes=$(echo "$minutes" | sed 's/^0*//')
# Default to 0 if empty after removing zeros
[ -z "$hours" ] && hours=0
[ -z "$minutes" ] && minutes=0
echo $((hours * 60 + minutes))
return 0
}
# Function to check if current time is within scheduled window
is_time_in_range() {
local current_minutes="$1"
local start_minutes="$2"
local end_minutes="$3"
# Make sure all values are numeric
if ! [[ "$current_minutes" =~ ^[0-9]+$ ]] ||
! [[ "$start_minutes" =~ ^[0-9]+$ ]] ||
! [[ "$end_minutes" =~ ^[0-9]+$ ]]; then
log_message "Non-numeric values in time comparison: current=$current_minutes, start=$start_minutes, end=$end_minutes" "error"
return 1 # Not in range
fi
# Handle case where end time is on the next day
if [ "$end_minutes" -lt "$start_minutes" ]; then
if [ "$current_minutes" -ge "$start_minutes" ] || [ "$current_minutes" -lt "$end_minutes" ]; then
return 0 # In range
fi
else
if [ "$current_minutes" -ge "$start_minutes" ] && [ "$current_minutes" -lt "$end_minutes" ]; then
return 0 # In range
fi
fi
return 1 # Not in range
}
# Main function to check at boot time
boot_check() {
log_message "Performing boot-time cell lock check" "info"
# Check if scheduling is enabled
local enabled=$(uci -q get "$UCI_CONFIG.cell_lock.enabled")
if [ "$enabled" != "1" ]; then
log_message "Cell lock scheduling is disabled" "info"
update_status "disabled" "Cell lock scheduling is disabled" 0 0
return 0
fi
# 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 [ -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
fi
# Get current time
local current_time=$(date "+%H:%M")
log_message "Current time: $current_time, Start: $start_time, End: $end_time" "info"
# 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 boot check" "error"
update_status "error" "Failed to acquire token for boot check" 0 0
return 1
fi
# Check if current time is in the scheduled range
if is_time_in_range "$current_minutes" "$start_minutes" "$end_minutes"; then
log_message "Current time IS within scheduled window" "info"
# Get lock parameters from UCI
local lte_lock_params=$(uci -q get "$UCI_CONFIG.cell_lock.lte_lock")
local nr5g_lock_params=$(uci -q get "$UCI_CONFIG.cell_lock.nr5g_lock")
# Apply locks if parameters exist
local success=0
if [ -n "$lte_lock_params" ]; then
log_message "Applying LTE lock at boot: $lte_lock_params" "info"
local lte_cmd="AT+QNWLOCK=\"common/4g\",$lte_lock_params"
execute_at_command "$lte_cmd" 10 "$token_id"
if [ $? -eq 0 ]; then
log_message "LTE lock applied successfully at boot" "info"
success=1
else
log_message "Failed to apply LTE lock at boot" "error"
fi
fi
if [ -n "$nr5g_lock_params" ]; then
log_message "Applying NR5G lock at boot: $nr5g_lock_params" "info"
local nr5g_cmd="AT+QNWLOCK=\"common/5g\",$nr5g_lock_params"
execute_at_command "$nr5g_cmd" 10 "$token_id"
if [ $? -eq 0 ]; then
log_message "NR5G lock applied successfully at boot" "info"
success=1
else
log_message "Failed to apply NR5G lock at boot" "error"
fi
fi
# Apply persist settings
local lte_persist=$(uci -q get "$UCI_CONFIG.cell_lock.lte_persist")
local nr5g_persist=$(uci -q get "$UCI_CONFIG.cell_lock.nr5g_persist")
# Default to 0 if not set
lte_persist="${lte_persist:-0}"
nr5g_persist="${nr5g_persist:-0}"
log_message "Setting persistence at boot: LTE=$lte_persist, NR5G=$nr5g_persist" "info"
local persist_cmd="AT+QNWLOCK=\"save_ctrl\",$lte_persist,$nr5g_persist"
execute_at_command "$persist_cmd" 10 "$token_id"
# Reset network to apply changes
log_message "Resetting network connection to apply changes" "info"
execute_at_command "AT+COPS=2" 5 "$token_id"
sleep 2
execute_at_command "AT+COPS=0" 5 "$token_id"
# Mark as active
uci set "$UCI_CONFIG.cell_lock.active=1"
uci commit "$UCI_CONFIG"
update_status "active" "Cell lock scheduler is active - applied at boot" 1 1
else
log_message "Current time is NOT within scheduled window" "info"
# Remove any existing locks
log_message "Removing LTE lock at boot" "info"
execute_at_command 'AT+QNWLOCK="common/4g",0' 10 "$token_id"
log_message "Removing NR5G lock at boot" "info"
execute_at_command 'AT+QNWLOCK="common/5g",0' 10 "$token_id"
# Disable persistence
log_message "Disabling lock persistence at boot" "info"
execute_at_command 'AT+QNWLOCK="save_ctrl",0,0' 10 "$token_id"
# Reset network to apply changes
log_message "Resetting network connection to apply changes" "info"
execute_at_command "AT+COPS=2" 5 "$token_id"
sleep 2
execute_at_command "AT+COPS=0" 5 "$token_id"
# Mark as inactive
uci set "$UCI_CONFIG.cell_lock.active=0"
uci commit "$UCI_CONFIG"
update_status "inactive" "Cell lock scheduler is inactive - outside scheduled hours" 0 0
fi
# Release token
release_token "$token_id"
return 0
}
# Execute main function
log_message "====== STARTING BOOT-TIME CELL LOCK CHECK ======" "notice"
boot_check
log_message "====== COMPLETED BOOT-TIME CELL LOCK CHECK ======" "notice"

View File

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

View File

@@ -0,0 +1,303 @@
#!/bin/sh
# Set content type to JSON
echo "Content-type: application/json"
echo ""
# Configuration
UCI_CONFIG="quecmanager"
STATUS_FILE="/tmp/cell_lock_status.json"
LOG_DIR="/tmp/log/cell_lock"
LOG_FILE="$LOG_DIR/cell_lock.log"
SCRIPTS_DIR="/www/cgi-bin/quecmanager/cell-locking"
# Ensure log directory exists
mkdir -p "$LOG_DIR"
# Function to log messages
log_message() {
local message="$1"
local level="${2:-info}"
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_handler -p "daemon.$level" "$message"
}
# 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
}
# Function to store cell lock parameters from current settings
store_lock_params() {
# Get the current LTE lock status
local lte_cmd="AT+QNWLOCK=\"common/4g\""
local lte_output=$(sms_tool at "$lte_cmd" -t 5 2>&1)
if [ $? -eq 0 ]; then
# Extract parameters if locked
if ! echo "$lte_output" | grep -q '"common/4g",0'; then
local lte_params=$(echo "$lte_output" | grep -o '"common/4g",[^[:space:]]*' | cut -d',' -f2-)
uci set "$UCI_CONFIG.cell_lock.lte_lock=$lte_params"
log_message "Stored LTE lock params: $lte_params" "info"
else
# If not locked, clear the parameters
uci set "$UCI_CONFIG.cell_lock.lte_lock="
log_message "No active LTE lock, cleared parameters" "info"
fi
fi
# Get the current NR5G lock status
local nr5g_cmd="AT+QNWLOCK=\"common/5g\""
local nr5g_output=$(sms_tool at "$nr5g_cmd" -t 5 2>&1)
if [ $? -eq 0 ]; then
# Extract parameters if locked
if ! echo "$nr5g_output" | grep -q '"common/5g",0'; then
local nr5g_params=$(echo "$nr5g_output" | grep -o '"common/5g",[^[:space:]]*' | cut -d',' -f2-)
uci set "$UCI_CONFIG.cell_lock.nr5g_lock=$nr5g_params"
log_message "Stored NR5G lock params: $nr5g_params" "info"
else
# If not locked, clear the parameters
uci set "$UCI_CONFIG.cell_lock.nr5g_lock="
log_message "No active NR5G lock, cleared parameters" "info"
fi
fi
# Get the persist settings
local persist_cmd="AT+QNWLOCK=\"save_ctrl\""
local persist_output=$(sms_tool at "$persist_cmd" -t 5 2>&1)
if [ $? -eq 0 ]; then
# Extract parameters
local persist_params=$(echo "$persist_output" | 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" "info"
fi
# Commit changes
uci commit "$UCI_CONFIG"
return 0
}
# Function to update crontab
update_crontab() {
local enabled=$(uci -q get "$UCI_CONFIG.cell_lock.enabled")
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 [ -z "$start_time" ] || [ -z "$end_time" ]; then
log_message "Missing start or end time" "error"
return 1
fi
local start_hour=$(echo "$start_time" | cut -d':' -f1 | sed 's/^0//')
local start_min=$(echo "$start_time" | cut -d':' -f2 | sed 's/^0//')
local end_hour=$(echo "$end_time" | cut -d':' -f1 | sed 's/^0//')
local end_min=$(echo "$end_time" | cut -d':' -f2 | sed 's/^0//')
# Create new crontab excluding our entries
local new_crontab=$(crontab -l | grep -v "apply_lock.sh\|remove_lock.sh")
if [ "$enabled" = "1" ]; then
# Add our entries
new_crontab="$new_crontab
$start_min $start_hour * * * $SCRIPTS_DIR/apply_lock.sh
$end_min $end_hour * * * $SCRIPTS_DIR/remove_lock.sh"
fi
# Apply new crontab
echo "$new_crontab" | crontab -
log_message "Updated crontab with start=$start_time, end=$end_time" "info"
return 0
}
# Handle GET requests for status
if [ "$REQUEST_METHOD" = "GET" ]; then
log_message "Handling GET request for cell lock status" "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 for cell lock scheduling" "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)
else
POST_DATA=""
fi
# Try to parse JSON data
if [ -n "$POST_DATA" ] && command -v jsonfilter >/dev/null 2>&1; then
log_message "Parsing JSON data: $POST_DATA" "debug"
# Extract values from JSON
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
# 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
# Store current cell lock parameters before enabling scheduler
store_lock_params
# 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"
# Update crontab
update_crontab
# Check if currently in window and apply lock if needed
CURRENT_TIME=$(date "+%H:%M")
CURRENT_HOUR=$(echo "$CURRENT_TIME" | cut -d':' -f1 | sed 's/^0//')
CURRENT_MIN=$(echo "$CURRENT_TIME" | cut -d':' -f2 | sed 's/^0//')
START_HOUR=$(echo "$START_TIME" | cut -d':' -f1 | sed 's/^0//')
START_MIN=$(echo "$START_TIME" | cut -d':' -f2 | sed 's/^0//')
END_HOUR=$(echo "$END_TIME" | cut -d':' -f1 | sed 's/^0//')
END_MIN=$(echo "$END_TIME" | cut -d':' -f2 | sed 's/^0//')
# Convert to minutes for comparison
CURRENT_MINUTES=$((CURRENT_HOUR * 60 + CURRENT_MIN))
START_MINUTES=$((START_HOUR * 60 + START_MIN))
END_MINUTES=$((END_HOUR * 60 + END_MIN))
# Check if current time is in range
IN_RANGE=0
if [ $END_MINUTES -lt $START_MINUTES ]; then
# Overnight schedule
if [ $CURRENT_MINUTES -ge $START_MINUTES ] || [ $CURRENT_MINUTES -lt $END_MINUTES ]; then
IN_RANGE=1
fi
else
# Same day schedule
if [ $CURRENT_MINUTES -ge $START_MINUTES ] && [ $CURRENT_MINUTES -lt $END_MINUTES ]; then
IN_RANGE=1
fi
fi
# Apply lock if in range
if [ $IN_RANGE -eq 1 ]; then
log_message "Current time is within scheduled window, applying lock now" "info"
"$SCRIPTS_DIR/apply_lock.sh" &
else
log_message "Current time is outside scheduled window, will apply at scheduled time" "info"
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 set "$UCI_CONFIG.cell_lock.active=0"
uci commit "$UCI_CONFIG"
# Update crontab (removes entries)
update_crontab
# Remove any active locks
"$SCRIPTS_DIR/remove_lock.sh" &
echo "{\"status\":\"success\",\"message\":\"Scheduling disabled\"}"
log_message "Successfully disabled scheduling" "info"
fi
else
log_message "Failed to parse JSON data or no JSON data received" "error"
echo "{\"status\":\"error\",\"message\":\"Invalid request or missing JSON data\"}"
fi
exit 0
fi
# If no valid method was handled
echo "{\"status\":\"error\",\"message\":\"Invalid request method\"}"
log_message "Invalid request method: $REQUEST_METHOD" "error"
exit 1

View File

@@ -0,0 +1,198 @@
#!/bin/sh
# Cell Lock Remove Script - Called by crontab at end time
# Configuration
UCI_CONFIG="quecmanager"
LOG_DIR="/tmp/log/cell_lock"
LOG_FILE="$LOG_DIR/cell_lock.log"
STATUS_FILE="/tmp/cell_lock_status.json"
QUEUE_DIR="/tmp/at_queue"
TOKEN_FILE="$QUEUE_DIR/token"
MAX_TOKEN_WAIT=15
TOKEN_PRIORITY=5
# Ensure log directory exists
mkdir -p "$LOG_DIR"
# Function to log messages
log_message() {
local message="$1"
local level="${2:-info}"
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_remove -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" <<EOF2
{
"status": "$status",
"message": "$message",
"active": $active,
"locked": $locked,
"timestamp": $(date +%s)
}
EOF2
chmod 644 "$STATUS_FILE"
log_message "Status updated: $status - $message (active=$active, locked=$locked)"
}
# Function to execute AT command
execute_at_command() {
local cmd="$1"
local timeout="${2:-10}"
# 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
}
# Main function to remove cell lock
remove_cell_lock() {
log_message "Removing cell lock at scheduled end time"
# Mark as inactive in UCI
uci set "$UCI_CONFIG.cell_lock.active=0"
uci commit "$UCI_CONFIG"
# Update status
update_status "inactive" "Cell lock scheduler is inactive - scheduled end time reached" 0 0
# Remove LTE lock
log_message "Removing LTE lock"
execute_at_command 'AT+QNWLOCK="common/4g",0' 10
local lte_status=$?
# Remove NR5G lock
log_message "Removing NR5G lock"
execute_at_command 'AT+QNWLOCK="common/5g",0' 10
local nr5g_status=$?
# Disable persistence
log_message "Disabling lock persistence"
execute_at_command 'AT+QNWLOCK="save_ctrl",0,0' 10
# Reset network to apply changes
log_message "Resetting network connection to apply changes"
execute_at_command "AT+COPS=2" 5
sleep 2
execute_at_command "AT+COPS=0" 5
log_message "Cell lock removed at scheduled end time"
update_status "inactive" "Cell lock removed at scheduled end time" 0 0
return 0
}
# Execute main function
log_message "====== STARTING SCHEDULED CELL LOCK REMOVAL ======" "notice"
remove_cell_lock
log_message "====== COMPLETED SCHEDULED CELL LOCK REMOVAL ======" "notice"
EOF
# Copy to quecmanager directory to match crontab
cp "$SERVICES_DIR/apply_lock.sh" "$QUECMANAGER_DIR/"
cp "$SERVICES_DIR/remove_lock.sh" "$QUECMANAGER_DIR/"
else
echo "Scripts found in both locations or only in quecmanager directory"
fi
# Make all scripts executable
chmod +x "$SERVICES_DIR/"*.sh 2>/dev/null
chmod +x "$QUECMANAGER_DIR/"*.sh 2>/dev/null
echo "All scripts are now executable"
# 3. Create test status file
cat > "/tmp/cell_lock_status.json" << EOF
{
"status": "active",
"message": "Cell lock scheduler is active",
"active": 1,
"locked": 1,
"timestamp": $(date +%s)
}
EOF
chmod 644 "/tmp/cell_lock_status.json"
echo "Created test status file at /tmp/cell_lock_status.json"
# 4. Check crontab entries and fix if needed
CRONTAB=$(crontab -l)
APPLY_ENTRY=$(echo "$CRONTAB" | grep "apply_lock.sh")
REMOVE_ENTRY=$(echo "$CRONTAB" | grep "remove_lock.sh")
echo "Current crontab entries:"
echo "$CRONTAB" | grep -E "apply_lock.sh|remove_lock.sh"
# Extract times from UCI config
START_TIME=$(uci -q get quecmanager.cell_lock.start_time)
END_TIME=$(uci -q get quecmanager.cell_lock.end_time)
if [ -n "$START_TIME" ] && [ -n "$END_TIME" ]; then
START_HOUR=$(echo "$START_TIME" | cut -d':' -f1 | sed 's/^0//')
START_MIN=$(echo "$START_TIME" | cut -d':' -f2 | sed 's/^0//')
END_HOUR=$(echo "$END_TIME" | cut -d':' -f1 | sed 's/^0//')
END_MIN=$(echo "$END_TIME" | cut -d':' -f2 | sed 's/^0//')
echo "UCI times: Start=$START_HOUR:$START_MIN, End=$END_HOUR:$END_MIN"
# Verify that crontab entries match UCI config
NEW_CRONTAB=$(crontab -l | grep -v "apply_lock.sh\|remove_lock.sh")
NEW_CRONTAB="$NEW_CRONTAB
$START_MIN $START_HOUR * * * $QUECMANAGER_DIR/apply_lock.sh
$END_MIN $END_HOUR * * * $QUECMANAGER_DIR/remove_lock.sh"
echo "Setting crontab with correct paths and times..."
echo "$NEW_CRONTAB" | crontab -
echo "Updated crontab entries:"
crontab -l | grep -E "apply_lock.sh|remove_lock.sh"
else
echo "WARNING: Could not find start/end times in UCI config"
fi
# 5. Test run scripts to verify they work
echo "Testing apply_lock.sh script..."
"$QUECMANAGER_DIR/apply_lock.sh" &
PID=$!
sleep 2
if kill -0 $PID 2>/dev/null; then
echo "apply_lock.sh is running correctly"
# Let it complete
wait $PID
else
echo "WARNING: apply_lock.sh may have issues"
fi
# 6. Check if log file is being created
if [ -f "/tmp/log/cell_lock/cell_lock.log" ]; then
echo "Log file exists. Last few entries:"
tail -n 10 "/tmp/log/cell_lock/cell_lock.log"
else
echo "WARNING: Log file not found"
fi

View File

@@ -0,0 +1,60 @@
#!/bin/sh
# Log rotation script for cell lock logs
# Configuration
LOG_DIR="/tmp/log/cell_lock"
LOG_FILE="$LOG_DIR/cell_lock.log"
MAX_LOG_SIZE=500 # KB
MAX_LOG_FILES=3 # Number of old log files to keep
# Ensure log directory exists
mkdir -p "$LOG_DIR"
# Function to log message
log_message() {
local message="$1"
local level="${2:-info}"
local component="log_rotation"
local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
# Use logger directly
logger -t "cell_lock_$component" -p "daemon.$level" "$message"
}
# Check if log file exists and its size
if [ -f "$LOG_FILE" ]; then
log_size=$(du -k "$LOG_FILE" | cut -f1)
if [ $log_size -gt $MAX_LOG_SIZE ]; then
log_message "Log file size ($log_size KB) exceeds maximum ($MAX_LOG_SIZE KB), rotating" "info"
# Rotate old logs
if [ -f "$LOG_FILE.2" ]; then
mv "$LOG_FILE.2" "$LOG_FILE.3"
fi
if [ -f "$LOG_FILE.1" ]; then
mv "$LOG_FILE.1" "$LOG_FILE.2"
fi
if [ -f "$LOG_FILE" ]; then
mv "$LOG_FILE" "$LOG_FILE.1"
fi
# Create a new empty log file
touch "$LOG_FILE"
chmod 644 "$LOG_FILE"
# Log rotation complete
log_message "Log rotation completed successfully" "info"
echo "$(date '+%Y-%m-%d %H:%M:%S') [info] [log_rotation] Log file rotated due to size" >> "$LOG_FILE"
else
log_message "Log file size ($log_size KB) within limits, no rotation needed" "debug"
fi
else
log_message "Log file does not exist, creating it" "info"
touch "$LOG_FILE"
chmod 644 "$LOG_FILE"
echo "$(date '+%Y-%m-%d %H:%M:%S') [info] [log_rotation] New log file created" >> "$LOG_FILE"
fi

View File

@@ -0,0 +1,111 @@
#!/bin/sh
# Script to update crontab entries for cell lock scheduling
# Configuration
UCI_CONFIG="quecmanager"
LOG_DIR="/tmp/log/cell_lock"
LOG_FILE="$LOG_DIR/cell_lock.log"
SCRIPTS_DIR="/www/cgi-bin/quecmanager/cell-locking"
LOCK_SCRIPT="$SCRIPTS_DIR/apply_lock.sh"
UNLOCK_SCRIPT="$SCRIPTS_DIR/remove_lock.sh"
TEMP_CRONTAB="/tmp/cell_lock_crontab"
ROTATE_SIZE=500 # KB before log rotation
# Ensure log directory exists
mkdir -p "$LOG_DIR"
# Enhanced log_message function
log_message() {
local message="$1"
local level="${2:-info}"
local component="update_crontab"
local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
local pid=$$
# Check if log file is too large (>500KB) and rotate if needed
if [ -f "$LOG_FILE" ] && [ $(du -k "$LOG_FILE" | cut -f1) -gt $ROTATE_SIZE ]; then
mv "$LOG_FILE" "$LOG_FILE.old"
touch "$LOG_FILE"
chmod 644 "$LOG_FILE"
fi
# Format: [timestamp] [level] [component] [pid] message
echo "[$timestamp] [$level] [$component] [$pid] $message" >> "$LOG_FILE"
# Also log to system log with appropriate priority
case "$level" in
debug) logger -t "cell_lock_$component" -p daemon.debug "$message" ;;
info) logger -t "cell_lock_$component" -p daemon.info "$message" ;;
notice) logger -t "cell_lock_$component" -p daemon.notice "$message" ;;
warn) logger -t "cell_lock_$component" -p daemon.warning "$message" ;;
error) logger -t "cell_lock_$component" -p daemon.err "$message" ;;
crit) logger -t "cell_lock_$component" -p daemon.crit "$message" ;;
*) logger -t "cell_lock_$component" -p daemon.info "$message" ;;
esac
}
# Function to update crontab
update_crontab() {
log_message "Starting crontab update process" "info"
local enabled=$(uci -q get "$UCI_CONFIG.cell_lock.enabled")
# Create a clean temporary crontab file
crontab -l | grep -v "$SCRIPTS_DIR/" > "$TEMP_CRONTAB" 2>/dev/null
if [ "$enabled" = "1" ]; then
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 [ -z "$start_time" ] || [ -z "$end_time" ]; then
log_message "Missing start or end time in configuration" "error"
return 1
fi
log_message "Scheduling cell locks with start=$start_time, end=$end_time" "info"
local start_hour=$(echo "$start_time" | cut -d':' -f1)
local start_minute=$(echo "$start_time" | cut -d':' -f2)
local end_hour=$(echo "$end_time" | cut -d':' -f1)
local end_minute=$(echo "$end_time" | cut -d':' -f2)
# Remove leading zeros
start_hour=$(echo "$start_hour" | sed 's/^0//')
start_minute=$(echo "$start_minute" | sed 's/^0//')
end_hour=$(echo "$end_hour" | sed 's/^0//')
end_minute=$(echo "$end_minute" | sed 's/^0//')
# Add crontab entries for lock and unlock
log_message "Adding crontab entry for start time: $start_minute $start_hour * * *" "debug"
echo "$start_minute $start_hour * * * $LOCK_SCRIPT" >> "$TEMP_CRONTAB"
log_message "Adding crontab entry for end time: $end_minute $end_hour * * *" "debug"
echo "$end_minute $end_hour * * * $UNLOCK_SCRIPT" >> "$TEMP_CRONTAB"
log_message "Added crontab entries for start time ($start_time) and end time ($end_time)" "info"
else
log_message "Cell lock scheduling is disabled, removing crontab entries" "info"
fi
# Apply the new crontab
log_message "Applying updated crontab" "debug"
crontab "$TEMP_CRONTAB"
local crontab_status=$?
if [ $crontab_status -eq 0 ]; then
log_message "Crontab updated successfully" "info"
else
log_message "Failed to update crontab (status: $crontab_status)" "error"
fi
# Clean up
rm -f "$TEMP_CRONTAB"
return $crontab_status
}
# Execute the function
log_message "====== STARTING CRONTAB UPDATE ======" "notice"
update_crontab
log_message "====== COMPLETED CRONTAB UPDATE ======" "notice"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,11 @@
#!/bin/sh
export HOME=/tmp/home
# Create named pipe for speedtest output if it doesn't exist
[ ! -p /tmp/realtime_spd.json ] && mkfifo /tmp/realtime_spd.json
# Run speedtest in background
/usr/bin/speedtest --accept-license -f json -p yes --progress-update-interval=100 > /tmp/realtime_spd.json
# Remove named pipe
rm /tmp/realtime_spd.json

View File

@@ -0,0 +1,29 @@
#!/bin/sh
# Location: /www/cgi-bin/quecmanager/home/speedtest/speedtest_status.sh
STATUS_FILE="/tmp/speedtest_status.json"
FINAL_RESULT="/tmp/speedtest_final.json"
echo "Content-Type: application/json"
echo "Cache-Control: no-cache, no-store, must-revalidate"
echo "Pragma: no-cache"
echo "Expires: 0"
echo ""
# Check if the test is completed and we have a final result
if [ -f "$FINAL_RESULT" ] && [ -r "$FINAL_RESULT" ] && [ -s "$FINAL_RESULT" ]; then
# Return the saved final result
cat $FINAL_RESULT
elif [ -f "$STATUS_FILE" ]; then
# Check if the file is readable and not empty
if [ -r "$STATUS_FILE" ] && [ -s "$STATUS_FILE" ]; then
# Return current status if test is running
cat $STATUS_FILE
else
# File exists but is empty or not readable
echo '{"status": "pending", "message": "Test initializing..."}'
fi
else
# Indicate no active test
echo '{"status": "not_running"}'
fi

View File

@@ -0,0 +1,13 @@
#!/bin/sh
echo "Content-Type: text/event-stream"
echo "Cache-Control: no-cache"
echo "Connection: keep-alive"
echo ""
# Use cat to read from the FIFO
cat /tmp/realtime_spd.json | while read line; do
echo "data: $line"
echo
sleep 0.1
done

View File

@@ -0,0 +1,36 @@
#!/bin/sh
# Location: /www/cgi-bin/quecmanager/home/speedtest/start_speedtest.sh
STATUS_FILE="/tmp/speedtest_status.json"
FINAL_RESULT="/tmp/speedtest_final.json"
# Set content type header
echo "Content-Type: application/json"
echo ""
# Remove any existing status files
rm -f $STATUS_FILE
rm -f $FINAL_RESULT
# Initialize status file
echo '{"status": "starting"}' > $STATUS_FILE
chmod 644 $STATUS_FILE
# Run speedtest in background and pipe output to status file
(
export HOME=/tmp/home
/usr/bin/speedtest --accept-license -f json -p yes --progress-update-interval=100 | \
while IFS= read -r line; do
# Update status file with latest JSON data
echo "$line" > $STATUS_FILE
# If this is a result line, also save it as the final result
if echo "$line" | grep -q '"type":"result"'; then
echo "$line" > $FINAL_RESULT
chmod 644 $FINAL_RESULT
fi
done
) &
# Return immediate success response
echo '{"status":"started"}'

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -0,0 +1,157 @@
#!/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)
local paused=$(uci -q get "quecprofiles.$idx.paused" 2>/dev/null)
# Debug output
log_message "Retrieved for $idx: name=$name, iccid=$iccid, apn=$apn, paused=$paused"
# 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}")
paused=$(sanitize_for_json "${paused:-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}\"paused\":\"${paused}\""
profile_json="${profile_json}}"
# Add comma if not first
if [ $first -eq 0 ]; then
json_output="${json_output},"
else
first=0
fi
# Add profile to output
json_output="${json_output}${profile_json}"
count=$((count+1))
done
# Complete the JSON response
local response="{\"status\":\"success\",\"profiles\":[${json_output}]}"
# Save the response for debugging
echo "$response" > /tmp/list_profiles_response.json
echo "$response"
log_message "Found and returned $count profiles"
return 0
}
# Start fresh error log
echo "=== List Profiles Run $(date) ===" > /tmp/list_profiles_error.log
# Main execution
{
get_profiles
} || {
# Error handler
output_error "Failed to retrieve profiles"
}

View File

@@ -0,0 +1,356 @@
#!/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'
set quecprofiles.@profile[-1].paused='0'
commit quecprofiles
EOF
# Check if the operation was successful
if [ $? -eq 0 ]; then
log_message "Successfully created profile '$name' for ICCID $iccid"
return 0
else
log_message "Failed to create profile '$name'" "error"
return 1
fi
}
# Output debug info
log_message "Received create profile request" "debug"
# Ensure UCI config exists
if [ ! -f /etc/config/quecprofiles ]; then
# Create initial config file
cat >/etc/config/quecprofiles <<EOF
config quecprofiles 'settings'
option check_interval '60'
option enable_autoswitch '1'
option apply_priority '20'
EOF
log_message "Created initial quecprofiles config file"
fi
# Get POST data
if [ "$REQUEST_METHOD" = "POST" ]; then
# Get content length
CONTENT_LENGTH=$(echo "$CONTENT_LENGTH" | tr -cd '0-9')
if [ -n "$CONTENT_LENGTH" ]; then
# Read POST data
POST_DATA=$(dd bs=1 count=$CONTENT_LENGTH 2>/dev/null)
# Debug log
log_message "Received POST data: $POST_DATA" "debug"
# Parse JSON with jsonfilter if available
if command -v jsonfilter >/dev/null 2>&1; then
name=$(echo "$POST_DATA" | jsonfilter -e '@.name' 2>/dev/null)
iccid=$(echo "$POST_DATA" | jsonfilter -e '@.iccid' 2>/dev/null)
imei=$(echo "$POST_DATA" | jsonfilter -e '@.imei' 2>/dev/null)
apn=$(echo "$POST_DATA" | jsonfilter -e '@.apn' 2>/dev/null)
pdp_type=$(echo "$POST_DATA" | jsonfilter -e '@.pdp_type' 2>/dev/null)
lte_bands=$(echo "$POST_DATA" | jsonfilter -e '@.lte_bands' 2>/dev/null)
sa_nr5g_bands=$(echo "$POST_DATA" | jsonfilter -e '@.sa_nr5g_bands' 2>/dev/null)
nsa_nr5g_bands=$(echo "$POST_DATA" | jsonfilter -e '@.nsa_nr5g_bands' 2>/dev/null)
network_type=$(echo "$POST_DATA" | jsonfilter -e '@.network_type' 2>/dev/null)
ttl=$(echo "$POST_DATA" | jsonfilter -e '@.ttl' 2>/dev/null)
log_message "Parsed JSON data for profile: $name" "debug"
else
# If jsonfilter is not available, try basic parsing
# This is less reliable but might work for simple cases
name=$(echo "$POST_DATA" | grep -o '"name":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
iccid=$(echo "$POST_DATA" | grep -o '"iccid":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
imei=$(echo "$POST_DATA" | grep -o '"imei":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
apn=$(echo "$POST_DATA" | grep -o '"apn":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
pdp_type=$(echo "$POST_DATA" | grep -o '"pdp_type":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
lte_bands=$(echo "$POST_DATA" | grep -o '"lte_bands":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
sa_nr5g_bands=$(echo "$POST_DATA" | grep -o '"sa_nr5g_bands":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
nsa_nr5g_bands=$(echo "$POST_DATA" | grep -o '"nsa_nr5g_bands":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
network_type=$(echo "$POST_DATA" | grep -o '"network_type":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
ttl=$(echo "$POST_DATA" | grep -o '"ttl":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"')
log_message "Basic parsing for profile: $name" "warn"
fi
else
log_message "No content length specified" "error"
output_json "error" "No data received"
fi
else
# URL parameters for GET requests (less secure, but supported for testing)
name=$(echo "$QUERY_STRING" | grep -o 'name=[^&]*' | cut -d'=' -f2)
iccid=$(echo "$QUERY_STRING" | grep -o 'iccid=[^&]*' | cut -d'=' -f2)
imei=$(echo "$QUERY_STRING" | grep -o 'imei=[^&]*' | cut -d'=' -f2)
apn=$(echo "$QUERY_STRING" | grep -o 'apn=[^&]*' | cut -d'=' -f2)
pdp_type=$(echo "$QUERY_STRING" | grep -o 'pdp_type=[^&]*' | cut -d'=' -f2)
lte_bands=$(echo "$QUERY_STRING" | grep -o 'lte_bands=[^&]*' | cut -d'=' -f2)
sa_nr5g_bands=$(echo "$QUERY_STRING" | grep -o 'sa_nr5g_bands=[^&]*' | cut -d'=' -f2)
nsa_nr5g_bands=$(echo "$QUERY_STRING" | grep -o 'nsa_nr5g_bands=[^&]*' | cut -d'=' -f2)
network_type=$(echo "$QUERY_STRING" | grep -o 'network_type=[^&]*' | cut -d'=' -f2)
ttl=$(echo "$QUERY_STRING" | grep -o 'ttl=[^&]*' | cut -d'=' -f2)
# URL decode values
name=$(echo "$name" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
iccid=$(echo "$iccid" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
imei=$(echo "$imei" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
apn=$(echo "$apn" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
pdp_type=$(echo "$pdp_type" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
lte_bands=$(echo "$lte_bands" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
sa_nr5g_bands=$(echo "$sa_nr5g_bands" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
nsa_nr5g_bands=$(echo "$nsa_nr5g_bands" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
network_type=$(echo "$network_type" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
ttl=$(echo "$ttl" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
log_message "Using URL parameters" "warn"
fi
# Sanitize inputs
name=$(sanitize "${name:-}")
iccid=$(sanitize "${iccid:-}")
imei=$(sanitize "${imei:-}")
apn=$(sanitize "${apn:-}")
pdp_type=$(sanitize "${pdp_type:-IP}")
lte_bands=$(sanitize "${lte_bands:-}")
sa_nr5g_bands=$(sanitize "${sa_nr5g_bands:-}")
nsa_nr5g_bands=$(sanitize "${nsa_nr5g_bands:-}")
network_type=$(sanitize "${network_type:-LTE}")
ttl=$(sanitize "${ttl:-0}") # Default to 0 (disabled)
# Output debug info
log_message "Creating profile: $name, ICCID: $iccid, IMEI: $imei, APN: $apn" "debug"
# Validate required inputs
if [ -z "$name" ]; then
log_message "Profile name is missing" "error"
output_json "error" "Profile name is required"
fi
if [ -z "$iccid" ]; then
log_message "ICCID is missing" "error"
output_json "error" "ICCID is required"
fi
if [ -z "$apn" ]; then
log_message "APN is missing" "error"
output_json "error" "APN is required"
fi
# Validate input formats
if ! validate_iccid "$iccid"; then
log_message "Invalid ICCID format: $iccid" "error"
output_json "error" "Invalid ICCID format. It should be 10-20 digits."
fi
if ! validate_imei "$imei"; then
log_message "Invalid IMEI format: $imei" "error"
output_json "error" "Invalid IMEI format. It should be exactly 15 digits."
fi
if ! validate_bands "$lte_bands"; then
log_message "Invalid LTE bands format: $lte_bands" "error"
output_json "error" "Invalid LTE bands format. Use comma-separated numbers (e.g., 1,3,7)"
fi
if ! validate_bands "$sa_nr5g_bands"; then
log_message "Invalid SA NR5G bands format: $sa_nr5g_bands" "error"
output_json "error" "Invalid SA NR5G bands format. Use comma-separated numbers (e.g., 41,78)"
fi
if ! validate_bands "$nsa_nr5g_bands"; then
log_message "Invalid NSA NR5G bands format: $nsa_nr5g_bands" "error"
output_json "error" "Invalid NSA NR5G bands format. Use comma-separated numbers (e.g., 1,79)"
fi
if ! validate_network_type "$network_type"; then
log_message "Invalid network type: $network_type" "error"
output_json "error" "Invalid network type. Use 'LTE', 'NR5G', or 'LTE:NR5G'"
fi
if ! validate_pdp_type "$pdp_type"; then
log_message "Invalid PDP type: $pdp_type" "error"
output_json "error" "Invalid PDP type. Use 'IP', 'IPV6', or 'IPV4V6'"
fi
if ! validate_ttl "$ttl"; then
log_message "Invalid TTL value: $ttl" "error"
output_json "error" "Invalid TTL value. It should be a number between 0 and 255."
fi
# Check for duplicates
check_duplicate_profile "$name" "$iccid"
dup_status=$?
if [ $dup_status -eq 1 ]; then
log_message "Duplicate profile name: $name" "error"
output_json "error" "A profile with this name already exists"
elif [ $dup_status -eq 2 ]; then
log_message "Duplicate ICCID: $iccid" "error"
output_json "error" "A profile with this ICCID already exists"
fi
# Create the profile
if create_profile "$name" "$iccid" "$imei" "$apn" "$pdp_type" "$lte_bands" "$sa_nr5g_bands" "$nsa_nr5g_bands" "$network_type" "$ttl"; then
# Trigger immediate profile application
touch "/tmp/quecprofiles_check"
chmod 644 "/tmp/quecprofiles_check"
log_message "Triggered immediate profile check after creation" "info"
# Create profile data JSON for return - WITHOUT outer curly braces
profile_data="\"name\":\"$name\",\"iccid\":\"$iccid\",\"imei\":\"$imei\",\"apn\":\"$apn\",\"pdp_type\":\"$pdp_type\",\"lte_bands\":\"$lte_bands\",\"sa_nr5g_bands\":\"$sa_nr5g_bands\",\"nsa_nr5g_bands\":\"$nsa_nr5g_bands\",\"network_type\":\"$network_type\",\"ttl\":\"$ttl\""
# Wrap the data field in curly braces inside output_json
output_json "success" "Profile created successfully" "{$profile_data}"
else
output_json "error" "Failed to create profile. Please check system logs."
fi

View File

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

View File

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

View File

@@ -0,0 +1,193 @@
#!/bin/sh
# Location: /www/cgi-bin/quecmanager/profiles/toggle_pause.sh
# Set content type to JSON
echo -n ""
echo "Content-type: application/json"
echo ""
# Configuration
CHECK_TRIGGER="/tmp/quecprofiles_check"
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" "toggle_pause: $1"
}
# Function to update status file directly - used when pausing a profile
update_status_to_paused() {
local profile_name="$1"
# Create JSON status for paused profile
cat > "$STATUS_FILE" <<EOF
{
"status": "paused",
"message": "Profile is paused. Resume the profile to apply settings.",
"profile": "$profile_name",
"progress": 0,
"timestamp": $(date +%s)
}
EOF
# Create simple track file for easy checking
echo "paused:$profile_name:0" > "$TRACK_FILE"
chmod 644 "$TRACK_FILE" "$STATUS_FILE"
log_message "Status updated: paused - Profile is paused ($profile_name)" "info"
}
# 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 toggle pause state
toggle_pause_state() {
local profile_index="$1"
local paused="$2" # 0 or 1
local profile_name=$(uci -q get quecprofiles.$profile_index.name)
# Update the profile in UCI config
uci -q batch <<EOF
set quecprofiles.$profile_index.paused='$paused'
commit quecprofiles
EOF
# Check if the operation was successful
if [ $? -eq 0 ]; then
if [ "$paused" = "1" ]; then
log_message "Successfully paused profile '$profile_name'" "info"
# Immediately update status to paused without waiting for daemon
update_status_to_paused "$profile_name"
return 0
else
log_message "Successfully resumed profile '$profile_name'" "info"
# Touch the check trigger file to force daemon to check ASAP
touch "$CHECK_TRIGGER"
chmod 644 "$CHECK_TRIGGER"
log_message "Triggered profile check for resumed profile '$profile_name'" "info"
return 0
fi
else
log_message "Failed to update pause state for profile '$profile_name'" "error"
return 1
fi
}
# Output debug info
log_message "Received toggle pause 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
iccid=""
paused=""
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)
paused=$(echo "$POST_DATA" | jsonfilter -e '@.paused' 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 '"')
paused=$(echo "$POST_DATA" | grep -o '"paused":[0-1]' | head -1 | cut -d':' -f2)
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 requests (for testing)
iccid=$(echo "$QUERY_STRING" | grep -o 'iccid=[^&]*' | cut -d'=' -f2)
paused=$(echo "$QUERY_STRING" | grep -o 'paused=[^&]*' | cut -d'=' -f2)
# URL decode values
iccid=$(echo "$iccid" | sed 's/+/ /g;s/%\(..\)/\\x\1/g;' | xargs -0 printf "%b")
log_message "Using URL parameters: iccid=$iccid, paused=$paused" "debug"
fi
# Sanitize inputs
iccid=$(sanitize "${iccid:-}")
paused=$(sanitize "${paused:-}")
# Validate required inputs
if [ -z "$iccid" ]; then
log_message "ICCID is missing" "error"
output_json "error" "ICCID is required to identify the profile"
fi
# Validate pause state (must be 0 or 1)
if [ "$paused" != "0" ] && [ "$paused" != "1" ]; then
log_message "Invalid paused state: $paused" "error"
output_json "error" "Paused state must be 0 (resumed) or 1 (paused)"
fi
# Find profile to toggle
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)
# Toggle pause state
if toggle_pause_state "$profile_index" "$paused"; then
if [ "$paused" = "1" ]; then
log_message "Profile paused successfully: $profile_name" "info"
output_json "success" "Profile paused successfully" "{\"iccid\":\"$iccid\",\"name\":\"$profile_name\",\"paused\":true}"
else
log_message "Profile resumed successfully: $profile_name" "info"
output_json "success" "Profile resumed successfully" "{\"iccid\":\"$iccid\",\"name\":\"$profile_name\",\"paused\":false}"
fi
else
log_message "Failed to update pause state for profile: $profile_name" "error"
output_json "error" "Failed to update profile status. Please check system logs."
fi

View File

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

View File

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