#!/bin/sh # Set content-type for JSON response echo "Content-type: application/json" echo "" # Define file paths and configuration QUEUE_FILE="/tmp/at_pipe.txt" LOCK_KEYWORD="FETCH_DATA_LOCK" MAX_WAIT=6 # Maximum seconds to wait for lock # Function to output error in JSON format output_error() { printf '{"error": "%s"}\n' "$1" exit 1 } # Function to clean and add lock with simplified timeout logic add_clean_lock() { local TIMESTAMP=$(date +%s) local WAIT_START=$(date +%s) while true; do local CURRENT_TIME=$(date +%s) # After MAX_WAIT seconds, forcibly remove any existing lock if [ $((CURRENT_TIME - WAIT_START)) -ge $MAX_WAIT ]; then # Remove any existing lock entries regardless of owner sed -i "/${LOCK_KEYWORD}/d" "$QUEUE_FILE" logger -t at_commands "Removed existing lock after $MAX_WAIT seconds timeout" fi # Add our lock entry printf '{"id":"%s","timestamp":"%s","command":"%s","status":"lock","pid":"%s","start_time":"%s"}\n' \ "${LOCK_KEYWORD}" \ "$(date '+%H:%M:%S')" \ "${LOCK_KEYWORD}" \ "$$" \ "$TIMESTAMP" >> "$QUEUE_FILE" # Verify our lock was written if grep -q "\"pid\":\"$$\".*\"start_time\":\"$TIMESTAMP\"" "$QUEUE_FILE"; then logger -t at_commands "Lock created by PID $$ at $TIMESTAMP" # Register cleanup handler trap 'remove_lock; exit' INT TERM EXIT return 0 fi # If we haven't exceeded MAX_WAIT, sleep and try again if [ $((CURRENT_TIME - WAIT_START)) -lt $MAX_WAIT ]; then sleep 1 else logger -t at_commands "Failed to acquire lock after $MAX_WAIT seconds" return 1 fi done } # Simple remove lock function that only removes our entry remove_lock() { sed -i "/\"pid\":\"$$\"/d" "$QUEUE_FILE" logger -t at_commands "Lock removed by PID $$" } # Improved 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$//' } # Enhanced AT command execution with retries execute_at_command() { local CMD="$1" local RETRY_COUNT=0 local MAX_RETRIES=3 local OUTPUT="" while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do OUTPUT=$(sms_tool at "$CMD" -t 4 2>/dev/null) if [ $? -eq 0 ] && [ -n "$OUTPUT" ]; then echo "$OUTPUT" return 0 fi RETRY_COUNT=$((RETRY_COUNT + 1)) [ $RETRY_COUNT -lt $MAX_RETRIES ] && sleep 1 done logger -t at_commands "Command failed after $MAX_RETRIES attempts: $CMD" return 1 } # Enhanced command processing function process_commands() { local commands="$1" local first=1 # Start JSON array printf '[' # Process each command for cmd in $commands; do # Add comma separator if not first item [ $first -eq 0 ] && printf ',' first=0 # Execute command with retries OUTPUT=$(execute_at_command "$cmd") local CMD_STATUS=$? # Properly escape both command and output for JSON ESCAPED_CMD=$(escape_json "$cmd") ESCAPED_OUTPUT=$(escape_json "$OUTPUT") # Format JSON object with proper escaping if [ $CMD_STATUS -eq 0 ]; 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 # Close JSON array printf ']\n' } # Main process wrapper with automatic lock handling main_with_clean_lock() { # Set timeout for the entire script ( sleep 60; kill -TERM $$ 2>/dev/null ) & TIMEOUT_PID=$! if ! add_clean_lock; then output_error "Failed to acquire lock for command processing" kill $TIMEOUT_PID 2>/dev/null exit 1 fi # Process commands process_commands "$COMMANDS" # Clean up remove_lock kill $TIMEOUT_PID 2>/dev/null } # Define command sets define_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?' 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"' } # Main execution define_command_sets # 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 # Default to set 1 if invalid or no set specified 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 # Execute main process with clean lock handling main_with_clean_lock