#!/system/bin/sh
# vend_agent.sh - agente de comandos remotos para la placa WM55L22.
# Sondea comando.php cada $INTERVAL seg; si hay una orden, ejecuta SOLO acciones de
# la LISTA BLANCA (cosas del entorno Android: diagnostico, brillo, updates) y devuelve
# el resultado. NO toca los puertos serie (no compite con com.weimi).
#
# AJUSTA estas variables (deben coincidir con tu servidor):
URL="https://vending.maexspe.com/comando.php"
TOKEN="maexspe-weimi-K7m2Rq9x"
CURL=/data/local/tmp/curl
SRVHOST=vending.maexspe.com
SRVIP=66.228.61.234                                  # IP fija (la placa no resuelve DNS)
RESOLVE="--resolve $SRVHOST:443:$SRVIP"
# Para auto-actualizacion (deja estos archivos en tu servidor):
SHURL="https://vending.maexspe.com/vend_run.sh"
AGENTURL="https://vending.maexspe.com/vend_agent.sh"
APKURL="https://vending.maexspe.com/maexspe_monitor.apk"
ALLOWDOM=maexspe.com   # SEGURIDAD: solo se instalan APKs de este dominio y sus subdominios
# Inventario (mismo que vend_run.sh):
DBPATH=/data/data/com.weimi/databases/Wm2022Database
DBTMP=/data/local/tmp/inv.db
DBURL="https://vending.maexspe.com/recibir_db.php"
SQLITE3=/system/bin/sqlite3   # ruta completa (bajo su no esta en el PATH)
INTERVAL=30        # sondeo cada 30s: liviano para la SIM (uso ocasional). Baja a 10 si quieres respuesta mas rapida.
# ---------------------------------------------------------------------------
CGET="$CURL -k -s -m 30 $RESOLVE"
LOG=/data/local/tmp/vend_agent.log

# Una sola instancia
LOCK=/data/local/tmp/vend_agent.lock
# Validar que el PID del lock SEA vend_agent.sh (no un PID reciclado tras reboot).
# Mismo bug que vend_run.sh: kill -0 a secas daba falso positivo. Ver vend_run.sh.
if [ -f "$LOCK" ]; then
  OLDPID=$(cat "$LOCK" 2>/dev/null)
  if [ -n "$OLDPID" ] && kill -0 "$OLDPID" 2>/dev/null \
     && tr '\0' ' ' < "/proc/$OLDPID/cmdline" 2>/dev/null | grep -q vend_agent.sh; then exit 0; fi
fi
echo $$ > "$LOCK"
echo "=== vend_agent start $(date '+%F %T') ===" >> "$LOG"

ejecutar() {
  CMD="$1"
  # primer token = accion; el resto = argumentos
  set -- $CMD
  ACCION="$1"; ARG1="$2"
  case "$ACCION" in
    estado)
      echo "fecha placa : $(date '+%F %T')"
      echo "captura     : $(pgrep -f vend_run.sh | tr '\n' ' ')"
      echo "strace      : $(pgrep strace | tr '\n' ' ')"
      echo "agente      : $(pgrep -f vend_agent.sh | tr '\n' ' ')"
      echo "com.weimi   : $(pgrep -f com.weimi:motor_serial | tr '\n' ' ')"
      echo "wifi wlan0  : $(ip -o -4 addr show wlan0 2>/dev/null | awk '{print $4}')"
      echo "bateria     : $(dumpsys battery 2>/dev/null | grep -i level | tr -d ' ')"
      echo "disco /data : $(df -h /data 2>/dev/null | tail -1)"
      echo "ultimas lineas de captura:"
      tail -3 /data/local/tmp/vend_capture.log 2>/dev/null
      ;;
    brillo)
      case "$ARG1" in ''|*[!0-9]*) echo "brillo invalido: '$ARG1' (usa 0-255)"; return ;; esac
      # En esta maquina com.weimi (kiosko) FUERZA el brillo de su ventana y pisa el
      # ajuste del sistema -> 'settings put screen_brightness' NO mueve la pantalla.
      # El control que SI funciona es el backlight fisico del panel (sysfs).
      BL=/sys/class/backlight/backlight/brightness
      if [ -e "$BL" ]; then
        echo "$ARG1" > "$BL" 2>/dev/null
        echo "backlight fisico -> $(cat /sys/class/backlight/backlight/actual_brightness 2>/dev/null) (max 255)"
      else
        echo "no existe $BL (revisa /sys/class/backlight/)"
      fi
      # tambien el ajuste del sistema, por si la pantalla recompone el brillo
      settings put system screen_brightness_mode 0 2>/dev/null
      settings put system screen_brightness "$ARG1" 2>/dev/null
      echo "ajuste sistema  -> $(settings get system screen_brightness 2>/dev/null)"
      ;;
    reiniciar_captura)
      pkill -9 -f vend_run.sh 2>/dev/null; pkill -9 strace 2>/dev/null
      rm -f /data/local/tmp/vend_run.lock
      sleep 1
      nohup sh /data/local/tmp/vend_run.sh >/dev/null 2>&1 &
      sleep 2
      echo "captura reiniciada. vend_run: $(pgrep -f vend_run.sh | tr '\n' ' ')"
      ;;
    subir_inventario)
      # foto completa y NO invasiva: .backup lee la BD viva (ve el WAL) y escribe una
      # copia limpia de 1 archivo, sin tocar los archivos vivos de com.weimi.
      rm -f "$DBTMP" "$DBTMP-wal" "$DBTMP-shm" 2>/dev/null
      # com.weimi vive en otro mount-namespace y bajo SELinux enforcing: el agente no la
      # ve. runcon (gafete su + categorias) + nsenter -t 1 -m (namespace del init) cruza
      # ambas barreras. Verificado 2026-06-16.
      PRIV="runcon u:r:su:s0:c0.c1023 nsenter -t 1 -m --"
      if [ -x "$SQLITE3" ]; then
        $PRIV "$SQLITE3" "$DBPATH" ".backup $DBTMP" 2>/dev/null
        echo "copia .backup: $([ -s "$DBTMP" ] && echo ok || echo FALLO)"
      else
        echo "sin sqlite3 en $SQLITE3: copia cruda (puede quedar atrasada por WAL)"
        $PRIV cp "$DBPATH" "$DBTMP" 2>/dev/null
      fi
      R=$($CURL -k -s -m 60 $RESOLVE -X POST --data-binary @"$DBTMP" "$DBURL?token=$TOKEN")
      echo "inventario subido. resp servidor: $R"
      ;;
    actualizar_sh)
      $CGET -o /data/local/tmp/vend_run.new "$SHURL" \
        && mv /data/local/tmp/vend_run.new /data/local/tmp/vend_run.sh \
        && chmod 755 /data/local/tmp/vend_run.sh \
        && echo "vend_run.sh actualizado. Usa 'reiniciar_captura' para aplicarlo." \
        || echo "fallo la descarga del vend_run.sh"
      ;;
    actualizar_agente)
      $CGET -o /data/local/tmp/vend_agent.new "$AGENTURL" \
        && mv /data/local/tmp/vend_agent.new /data/local/tmp/vend_agent.sh \
        && chmod 755 /data/local/tmp/vend_agent.sh \
        && echo "vend_agent.sh actualizado. Reinicia el agente: pkill -f vend_agent; (se relanza al boot)" \
        || echo "fallo la descarga del agente"
      ;;
    actualizar_apk)
      # descarga e instala DESACOPLADO (para no morir a media instalacion).
      # BUG 2026-06-23: el subshell de fondo heredaba la tuberia con la que el agente
      # captura el resultado (OUT=$(ejecutar ...)) -> $() se quedaba ESPERANDO a que el
      # "pm install" terminara, y si este se colgaba, CONGELABA todo el agente (y el
      # latido). Fix: redirigir TODOS los descriptores del subshell (1,2 y 0) fuera de la
      # tuberia con setsid, asi el agente devuelve al toque y el install corre suelto.
      $CGET -o /data/local/tmp/nuevo.apk "$APKURL" \
        && { setsid sh -c 'sleep 2; pm install -r /data/local/tmp/nuevo.apk' >/dev/null 2>&1 </dev/null & echo "APK descargado; instalando suelto (pm install -r)"; } \
        || echo "fallo la descarga del APK"
      ;;
    instalar_apk)
      # Instala un APK desde CUALQUIER URL: instalar_apk https://host/app.apk
      U="$ARG1"
      [ -z "$U" ] && { echo "uso: instalar_apk <url>"; return; }
      # La placa no resuelve DNS por curl -> sacamos el host y su IP via ping (resolver
      # del sistema) y se la pasamos con --resolve.
      H=$(echo "$U" | sed -E 's#^https?://([^/:]+).*#\1#')
      # SEGURIDAD: solo dominio propio y subdominios (rechaza cualquier otra URL).
      case "$H" in
        $ALLOWDOM|*.$ALLOWDOM) : ;;
        *) echo "RECHAZADO: '$H' no pertenece a $ALLOWDOM. Solo se permiten APKs de ese dominio."; return ;;
      esac
      IP=$(ping -c1 -W2 "$H" 2>/dev/null | sed -n 's/.*(\([0-9.][0-9.]*\)).*/\1/p' | head -1)
      R=""; [ -n "$IP" ] && R="--resolve $H:443:$IP --resolve $H:80:$IP"
      echo "descargando $U (host $H -> ${IP:-sin IP})"
      if $CURL -k -L -s -m 120 $R -o /data/local/tmp/remoto.apk "$U"; then
        echo "instalando..."
        pm install -r /data/local/tmp/remoto.apk 2>&1
      else
        echo "fallo la descarga (¿DNS del host $H? IP=$IP)"
      fi
      ;;
    shell)
      # AVANZADO: ejecuta el resto como comando crudo (root). Util para diagnostico.
      SHCMD="${CMD#shell }"
      eval "$SHCMD" 2>&1
      ;;
    *)
      echo "accion no reconocida: '$ACCION'. Validas: estado, brillo N, reiniciar_captura, subir_inventario, actualizar_sh, actualizar_agente, actualizar_apk, instalar_apk <url>, shell <cmd>"
      ;;
  esac
}

while true; do
  RESP=$($CGET "$URL?token=$TOKEN&rol=placa")
  if [ -n "$RESP" ]; then
    ID=$(echo "$RESP" | head -1)
    CMD=$(echo "$RESP" | sed -n '2,$p')
    echo "$(date '+%F %T') ejecutando id=$ID cmd=$CMD" >> "$LOG"
    OUT=$(ejecutar "$CMD" 2>&1)
    echo "$OUT" | $CURL -k -s -m 60 $RESOLVE -X POST --data-binary @- \
      "$URL?token=$TOKEN&rol=placa&id=$ID" >/dev/null 2>&1
  fi
  sleep "$INTERVAL"
done
