Merging Beta 2.0.0 Release Candidate
This commit is contained in:
@@ -0,0 +1,266 @@
|
||||
#!/bin/sh
|
||||
# AT Queue Client for OpenWRT
|
||||
# Located in /www/cgi-bin/services/at_queue_client
|
||||
|
||||
QUEUE_DIR="/tmp/at_queue"
|
||||
RESULTS_DIR="$QUEUE_DIR/results"
|
||||
QUEUE_MANAGER="/www/cgi-bin/services/at_queue_manager.sh"
|
||||
POLL_INTERVAL=0.01
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 [options] <AT command>"
|
||||
echo "Options:"
|
||||
echo " -w Wait for command completion"
|
||||
echo " -t Timeout in seconds (default: 240)"
|
||||
echo " -h Show this help message"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Output JSON response
|
||||
output_json() {
|
||||
local content="$1"
|
||||
local headers="${2:-1}" # Default to showing headers
|
||||
echo "$content"
|
||||
}
|
||||
|
||||
# URL decode function
|
||||
urldecode() {
|
||||
local encoded="$1"
|
||||
logger -t at_queue -p daemon.debug "urldecode: input='$encoded'"
|
||||
|
||||
# Handle %2B -> + and %22 -> " conversions
|
||||
local decoded="${encoded//%2B/+}"
|
||||
decoded="${decoded//%22/\"}"
|
||||
# Then handle other encoded characters
|
||||
decoded=$(printf '%b' "${decoded//%/\\x}")
|
||||
|
||||
logger -t at_queue -p daemon.debug "urldecode: output='$decoded'"
|
||||
echo "$decoded"
|
||||
}
|
||||
|
||||
# Extract command ID from response with improved error handling
|
||||
get_command_id() {
|
||||
local response="$1"
|
||||
echo "DEBUG: Raw response: '$response'" >&2
|
||||
|
||||
# Strip any headers from response
|
||||
local json_response=$(echo "$response" | sed -n '/^{/,$p')
|
||||
echo "DEBUG: JSON portion: '$json_response'" >&2
|
||||
|
||||
# Try to extract command_id using grep and sed instead of jsonfilter
|
||||
local cmd_id=$(echo "$json_response" | grep -o '"command_id":"[^"]*"' | sed 's/"command_id":"//;s/"$//')
|
||||
|
||||
if [ -n "$cmd_id" ]; then
|
||||
echo "$cmd_id"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Fallback to jsonfilter if available
|
||||
echo "DEBUG: Trying jsonfilter as fallback" >&2
|
||||
local cmd_id_jsonfilter=$(echo "$json_response" | jsonfilter -e '@.command_id' 2>/dev/null)
|
||||
|
||||
if [ -n "$cmd_id_jsonfilter" ]; then
|
||||
echo "$cmd_id_jsonfilter"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "ERROR: Failed to extract command ID from response" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
# Normalize AT command
|
||||
normalize_at_command() {
|
||||
local cmd="$1"
|
||||
logger -t at_queue -p daemon.debug "normalize: input='$cmd'"
|
||||
|
||||
# URL decode the command
|
||||
cmd=$(urldecode "$cmd")
|
||||
logger -t at_queue -p daemon.debug "normalize: after urldecode='$cmd'"
|
||||
|
||||
# Remove any carriage returns or newlines
|
||||
cmd=$(echo "$cmd" | tr -d '\r\n')
|
||||
logger -t at_queue -p daemon.debug "normalize: after cleanup='$cmd'"
|
||||
|
||||
# Trim leading/trailing whitespace while preserving quotes
|
||||
cmd=$(echo "$cmd" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||
logger -t at_queue -p daemon.debug "normalize: final output='$cmd'"
|
||||
|
||||
echo "$cmd"
|
||||
}
|
||||
|
||||
# Submit command with priority handling
|
||||
submit_command() {
|
||||
local cmd="$1"
|
||||
local priority=10
|
||||
|
||||
# Set high priority for QSCAN commands for faster processing
|
||||
if echo "$cmd" | grep -qi "AT+QSCAN"; then
|
||||
priority=1
|
||||
fi
|
||||
|
||||
# Submit using appropriate method
|
||||
if [ "${SCRIPT_NAME}" != "" ]; then
|
||||
# CGI mode - direct execution
|
||||
local escaped_cmd=$(echo "$cmd" | sed 's/"/\\"/g')
|
||||
QUERY_STRING="action=enqueue&command=${escaped_cmd}&priority=$priority" "$QUEUE_MANAGER"
|
||||
else
|
||||
# CLI mode
|
||||
"$QUEUE_MANAGER" enqueue "$cmd" "$priority"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if result exists with proper error handling
|
||||
check_result() {
|
||||
local cmd_id="$1"
|
||||
local show_headers="${2:-1}" # Add parameter for header control
|
||||
|
||||
if [ -f "$RESULTS_DIR/$cmd_id.json" ]; then
|
||||
local result_content=$(cat "$RESULTS_DIR/$cmd_id.json")
|
||||
if [ -z "$result_content" ]; then
|
||||
logger -t at_queue -p daemon.error "Empty result file for command ID: $cmd_id"
|
||||
local error_json="{\"error\":\"Empty result file\",\"command_id\":\"$cmd_id\"}"
|
||||
output_json "$error_json" "$show_headers"
|
||||
return 1
|
||||
fi
|
||||
output_json "$result_content" "$show_headers"
|
||||
return 0
|
||||
fi
|
||||
local error_json="{\"error\":\"Result not found\",\"command_id\":\"$cmd_id\"}"
|
||||
output_json "$error_json" "$show_headers"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Wait for command completion with optimized polling and better error handling
|
||||
wait_for_completion() {
|
||||
local cmd_id="$1"
|
||||
local timeout="$2"
|
||||
local show_headers="${3:-1}"
|
||||
local result_file="$RESULTS_DIR/$cmd_id.json"
|
||||
|
||||
if [ -z "$cmd_id" ]; then
|
||||
local error_json="{\"error\":\"Invalid command ID\"}"
|
||||
output_json "$error_json" "$show_headers"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# First quick check
|
||||
if [ -f "$result_file" ]; then
|
||||
output_json "$(cat "$result_file")" "$show_headers"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Wait with shorter polling interval
|
||||
local start_time=$(date +%s)
|
||||
local current_time
|
||||
|
||||
while true; do
|
||||
if [ -f "$result_file" ]; then
|
||||
output_json "$(cat "$result_file")" "$show_headers"
|
||||
return 0
|
||||
fi
|
||||
|
||||
current_time=$(date +%s)
|
||||
if [ $((current_time - start_time)) -ge "$timeout" ]; then
|
||||
break
|
||||
fi
|
||||
|
||||
sleep $POLL_INTERVAL
|
||||
done
|
||||
|
||||
local error_json=$(cat << EOF
|
||||
{
|
||||
"error": "Timeout waiting for completion",
|
||||
"command_id": "$cmd_id",
|
||||
"timeout": $timeout
|
||||
}
|
||||
EOF
|
||||
)
|
||||
output_json "$error_json" "$show_headers"
|
||||
return 1
|
||||
}
|
||||
|
||||
# CGI request handling
|
||||
if [ "${SCRIPT_NAME}" != "" ]; then
|
||||
# Output headers only once at the beginning
|
||||
echo "Content-Type: application/json"
|
||||
echo ""
|
||||
|
||||
# Parse query string
|
||||
eval $(echo "$QUERY_STRING" | sed 's/&/;/g')
|
||||
|
||||
# Handle different actions
|
||||
if [ -n "$command_id" ]; then
|
||||
# Get result for specific command ID
|
||||
check_result "$command_id" "0" # Don't show headers
|
||||
elif [ -n "$command" ]; then
|
||||
# URL decode and normalize the command
|
||||
command=$(urldecode "$command")
|
||||
command=$(normalize_at_command "$command")
|
||||
|
||||
# Check if it's a valid AT command
|
||||
if echo "$command" | grep -qi "^AT"; then
|
||||
# Submit command and get response
|
||||
response=$(submit_command "$command")
|
||||
cmd_id=$(get_command_id "$response")
|
||||
|
||||
if [ "$wait" = "1" ]; then
|
||||
if [ -n "$cmd_id" ]; then
|
||||
wait_for_completion "$cmd_id" "${timeout:-180}" "0" # Don't show headers
|
||||
else
|
||||
output_json "{\"error\":\"Failed to get command ID from response\",\"response\":\"$response\"}" "0"
|
||||
fi
|
||||
else
|
||||
output_json "$response" "0" # Don't show headers
|
||||
fi
|
||||
else
|
||||
output_json "{\"error\":\"Invalid AT command format\"}" "0"
|
||||
fi
|
||||
else
|
||||
output_json "{\"error\":\"No command or command_id specified\"}" "0"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# CLI processing
|
||||
wait_mode=0
|
||||
timeout=180
|
||||
|
||||
while getopts "wt:h" opt; do
|
||||
case $opt in
|
||||
w) wait_mode=1 ;;
|
||||
t) timeout="$OPTARG" ;;
|
||||
h) usage ;;
|
||||
?) usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
shift $((OPTIND-1))
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
# Combine remaining arguments into AT command
|
||||
command="$*"
|
||||
|
||||
# Validate AT command format
|
||||
if ! echo "$command" | grep -qi "^AT"; then
|
||||
echo "Error: Command must start with 'AT'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Submit command and get response
|
||||
response=$(submit_command "$command")
|
||||
cmd_id=$(get_command_id "$response")
|
||||
|
||||
if [ -z "$cmd_id" ]; then
|
||||
echo "Error: Failed to get command ID"
|
||||
echo "Response: $response"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $wait_mode -eq 1 ]; then
|
||||
wait_for_completion "$cmd_id" "$timeout"
|
||||
else
|
||||
echo "$response"
|
||||
fi
|
||||
@@ -0,0 +1,195 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Set content-type for JSON response
|
||||
echo "Content-type: application/json"
|
||||
echo ""
|
||||
|
||||
# Define paths and constants to match queue system
|
||||
QUEUE_DIR="/tmp/at_queue"
|
||||
QUEUE_MANAGER="/www/cgi-bin/services/at_queue_manager"
|
||||
LOCK_ID="FETCH_DATA_$(date +%s)_$$"
|
||||
TOKEN_FILE="$QUEUE_DIR/token"
|
||||
|
||||
# Logging function (minimized)
|
||||
log_message() {
|
||||
# Only log errors and critical info
|
||||
if [ "$1" = "error" ] || [ "$1" = "crit" ]; then
|
||||
logger -t at_queue -p "daemon.$1" "$2"
|
||||
fi
|
||||
}
|
||||
|
||||
# Enhanced JSON string escaping function
|
||||
escape_json() {
|
||||
printf '%s' "$1" | awk '
|
||||
BEGIN { RS="\n"; ORS="\\n" }
|
||||
{
|
||||
gsub(/\\/, "\\\\")
|
||||
gsub(/"/, "\\\"")
|
||||
gsub(/\r/, "")
|
||||
gsub(/\t/, "\\t")
|
||||
gsub(/\f/, "\\f")
|
||||
gsub(/\b/, "\\b")
|
||||
print
|
||||
}
|
||||
' | sed 's/\\n$//'
|
||||
}
|
||||
|
||||
# Acquire token directly (avoid CGI overhead)
|
||||
acquire_token() {
|
||||
local priority="${1:-10}"
|
||||
local max_attempts=10
|
||||
local attempt=0
|
||||
|
||||
while [ $attempt -lt $max_attempts ]; do
|
||||
# Check if token file exists
|
||||
if [ -f "$TOKEN_FILE" ]; then
|
||||
local current_holder=$(cat "$TOKEN_FILE" | jsonfilter -e '@.id' 2>/dev/null)
|
||||
local current_priority=$(cat "$TOKEN_FILE" | jsonfilter -e '@.priority' 2>/dev/null)
|
||||
local timestamp=$(cat "$TOKEN_FILE" | jsonfilter -e '@.timestamp' 2>/dev/null)
|
||||
local current_time=$(date +%s)
|
||||
|
||||
# Check for expired token (> 30 seconds old)
|
||||
if [ $((current_time - timestamp)) -gt 30 ] || [ -z "$current_holder" ]; then
|
||||
# Remove expired token
|
||||
rm -f "$TOKEN_FILE" 2>/dev/null
|
||||
elif [ $priority -lt $current_priority ]; then
|
||||
# Preempt lower priority token
|
||||
rm -f "$TOKEN_FILE" 2>/dev/null
|
||||
else
|
||||
# Try again
|
||||
sleep 0.1
|
||||
attempt=$((attempt + 1))
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# Try to create token file
|
||||
echo "{\"id\":\"$LOCK_ID\",\"priority\":$priority,\"timestamp\":$(date +%s)}" > "$TOKEN_FILE" 2>/dev/null
|
||||
chmod 644 "$TOKEN_FILE" 2>/dev/null
|
||||
|
||||
# Verify we got the token
|
||||
local holder=$(cat "$TOKEN_FILE" 2>/dev/null | jsonfilter -e '@.id' 2>/dev/null)
|
||||
if [ "$holder" = "$LOCK_ID" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
sleep 0.1
|
||||
attempt=$((attempt + 1))
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Release token directly
|
||||
release_token() {
|
||||
# Only remove if it's our token
|
||||
if [ -f "$TOKEN_FILE" ]; then
|
||||
local current_holder=$(cat "$TOKEN_FILE" | jsonfilter -e '@.id' 2>/dev/null)
|
||||
if [ "$current_holder" = "$LOCK_ID" ]; then
|
||||
rm -f "$TOKEN_FILE" 2>/dev/null
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Direct AT command execution with minimal overhead
|
||||
execute_at_command() {
|
||||
local CMD="$1"
|
||||
sms_tool at "$CMD" -t 3 2>/dev/null
|
||||
}
|
||||
|
||||
# Batch process all commands with a single token
|
||||
process_all_commands() {
|
||||
local commands="$1"
|
||||
local priority="${2:-10}"
|
||||
local first=1
|
||||
|
||||
# Acquire a single token for all commands
|
||||
if ! acquire_token "$priority"; then
|
||||
log_message "error" "Failed to acquire token for batch processing"
|
||||
# Return all failed responses
|
||||
printf '['
|
||||
first=1
|
||||
for cmd in $commands; do
|
||||
[ $first -eq 0 ] && printf ','
|
||||
first=0
|
||||
ESCAPED_CMD=$(escape_json "$cmd")
|
||||
printf '{"command":"%s","response":"Failed to acquire token","status":"error"}' "${ESCAPED_CMD}"
|
||||
done
|
||||
printf ']\n'
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Process all commands with the single token
|
||||
printf '['
|
||||
for cmd in $commands; do
|
||||
[ $first -eq 0 ] && printf ','
|
||||
first=0
|
||||
|
||||
OUTPUT=$(execute_at_command "$cmd")
|
||||
local CMD_STATUS=$?
|
||||
|
||||
ESCAPED_CMD=$(escape_json "$cmd")
|
||||
ESCAPED_OUTPUT=$(escape_json "$OUTPUT")
|
||||
|
||||
if [ $CMD_STATUS -eq 0 ] && [ -n "$OUTPUT" ]; then
|
||||
printf '{"command":"%s","response":"%s","status":"success"}' \
|
||||
"${ESCAPED_CMD}" \
|
||||
"${ESCAPED_OUTPUT}"
|
||||
else
|
||||
printf '{"command":"%s","response":"Command failed","status":"error"}' \
|
||||
"${ESCAPED_CMD}"
|
||||
fi
|
||||
done
|
||||
printf ']\n'
|
||||
|
||||
# Release token after all commands are done
|
||||
release_token
|
||||
return 0
|
||||
}
|
||||
|
||||
# Main execution with timeout and proper cleanup
|
||||
trap 'release_token; exit 1' INT TERM
|
||||
|
||||
# Command sets
|
||||
COMMAND_SET_1='AT+QUIMSLOT? AT+CNUM AT+COPS? AT+CIMI AT+ICCID AT+CGSN AT+CPIN? AT+CGDCONT? AT+CREG? AT+CFUN? AT+QENG="servingcell" AT+QTEMP AT+CGCONTRDP AT+QCAINFO AT+QRSRP AT+QMAP="WWAN" AT+C5GREG=2;+C5GREG? AT+CGREG=2;+CGREG? AT+QRSRQ AT+QSINR'
|
||||
COMMAND_SET_2='AT+CGDCONT? AT+CGCONTRDP AT+QNWPREFCFG="mode_pref" AT+QNWPREFCFG="nr5g_disable_mode" AT+QUIMSLOT? AT+CFUN=?'
|
||||
COMMAND_SET_3='AT+CGMI AT+CGMM AT+QGMR AT+CNUM AT+CIMI AT+ICCID AT+CGSN AT+QMAP="LANIP" AT+QMAP="WWAN" AT+QGETCAPABILITY'
|
||||
COMMAND_SET_4='AT+QMAP="MPDN_RULE" AT+QMAP="DHCPV4DNS" AT+QCFG="usbnet"'
|
||||
COMMAND_SET_5='AT+QRSRP AT+QRSRQ AT+QSINR AT+QCAINFO AT+QSPN'
|
||||
COMMAND_SET_6='AT+CEREG=2;+CEREG? AT+C5GREG=2;+C5GREG? AT+CPIN? AT+CGDCONT? AT+CGCONTRDP AT+QMAP="WWAN" AT+QRSRP AT+QTEMP AT+QNETRC?'
|
||||
COMMAND_SET_7='AT+QNWPREFCFG="policy_band" AT+QNWPREFCFG="lte_band";+QNWPREFCFG="nsa_nr5g_band";+QNWPREFCFG="nr5g_band"'
|
||||
COMMAND_SET_8='AT+QNWLOCK="common/4g" AT+QNWLOCK="common/5g" AT+QNWLOCK="save_ctrl"'
|
||||
|
||||
# Get command set from query string with validation
|
||||
COMMAND_SET=$(echo "$QUERY_STRING" | grep -o 'set=[1-8]' | cut -d'=' -f2 | tr -cd '0-9')
|
||||
if [ -z "$COMMAND_SET" ] || [ "$COMMAND_SET" -lt 1 ] || [ "$COMMAND_SET" -gt 8 ]; then
|
||||
COMMAND_SET=1
|
||||
fi
|
||||
|
||||
# Select the appropriate command set
|
||||
case "$COMMAND_SET" in
|
||||
1) COMMANDS="$COMMAND_SET_1";;
|
||||
2) COMMANDS="$COMMAND_SET_2";;
|
||||
3) COMMANDS="$COMMAND_SET_3";;
|
||||
4) COMMANDS="$COMMAND_SET_4";;
|
||||
5) COMMANDS="$COMMAND_SET_5";;
|
||||
6) COMMANDS="$COMMAND_SET_6";;
|
||||
7) COMMANDS="$COMMAND_SET_7";;
|
||||
8) COMMANDS="$COMMAND_SET_8";;
|
||||
esac
|
||||
|
||||
# Set priority based on content
|
||||
PRIORITY=10
|
||||
if echo "$COMMANDS" | grep -qi "AT+QSCAN"; then
|
||||
PRIORITY=1
|
||||
fi
|
||||
|
||||
# Process commands with timeout protection
|
||||
( sleep 60; kill -TERM $$ 2>/dev/null ) &
|
||||
TIMEOUT_PID=$!
|
||||
|
||||
process_all_commands "$COMMANDS" "$PRIORITY"
|
||||
|
||||
# Clean up
|
||||
kill $TIMEOUT_PID 2>/dev/null
|
||||
release_token
|
||||
Reference in New Issue
Block a user