From cdf28ee55c33966bdfbc5a50c6d6c17ab0d18ced Mon Sep 17 00:00:00 2001 From: Sergio Pernas Date: Wed, 23 Mar 2022 16:01:50 -0300 Subject: [PATCH] update --- bin/nodemecu | 84 ++++++++++++++++ bin/reset | 4 + envs | 31 ++++++ funciones | 270 +++++++++++++++++++++++++++++++++++++++++++++++++++ registrador | 80 +++++++++++++++ 5 files changed, 469 insertions(+) create mode 100755 bin/nodemecu create mode 100644 bin/reset create mode 100644 envs create mode 100644 funciones create mode 100755 registrador diff --git a/bin/nodemecu b/bin/nodemecu new file mode 100755 index 0000000..673e093 --- /dev/null +++ b/bin/nodemecu @@ -0,0 +1,84 @@ +#!/bin/bash +if [ $UID -ne 0 ]; then + echo "Ejecute 'sudo $0'" + exit +fi + +# LECTURA FICHERO DE CONFIGURACION + +config=/etc/nodemecu.conf +envs=/opt/nodemecu/envs +source $config +source $envs +source $funciones + +[ -z $1 ] && exit + + +com=$1 + +case $com in + + configurar) + echo "Complete los cambios a continuación: + " + echo "Nombre del dispositivo." + read -p "nombre: " -a entrada_usuario + funcion_configurar nombre ${entrada_usuario[@]} + echo "" + echo "Servidor de entrega de datos." + read -p "servidor (ej. https://url.del/servidor): " entrada_usuario + funcion_configurar servidor $entrada_usuario + echo "" + echo "Intervalo de toma de muestras, entre 1 y 60 minutos." + read -p "intervalo: " entrada_usuario + funcion_configurar intervalo $entrada_usuario + echo "" + echo "Creando juego de llaves" + source $config + comentario="$nombre $numero_serie" + generate_private_key "$comentario" + echo "" + echo "El modo de operación por defecto es 'test' y se envían datos simulados, cambie a 'sensores' con el comando 'sudo nodemecu modo sensores' + " + ;; + + nombre) + funcion_configurar $@ + ;; + servidor) + funcion_configurar $@ + ;; + intervalo) + funcion_configurar $@ + ;; + modo) + funcion_configurar $@ + ;; + captura) + + test_conf_nombre || exit + test_conf_intervalo || exit + test_conf_key || exit + funcion_captura $2 + ;; + monitor) + funcion_monitor + ;; + cllave) + if [ -z $nombre ]; then + echo "Debe establecer primero el nombre del dispositivo" + else + echo "Creando juego de llaves" + generate_private_key + fi + ;; + + + *) + exit + ;; + +esac + + diff --git a/bin/reset b/bin/reset new file mode 100644 index 0000000..1269e31 --- /dev/null +++ b/bin/reset @@ -0,0 +1,4 @@ +rm stack/* +rm historical/* +printf "" > errors.log +printf "0" > counter diff --git a/envs b/envs new file mode 100644 index 0000000..777db01 --- /dev/null +++ b/envs @@ -0,0 +1,31 @@ +# NO MODIFICAR ESTE FICHERO !!!!!!!!!! + +numero_serie=0000000083a999fd +curl_err=/tmp/curl_err +directorio_instalacion=/opt/nodemecu +registros=$directorio_instalacion/registros +historicos=$directorio_instalacion/historicos +log=/var/log/nodemecu_errors.log +registros_corruptos=$directorio_instalacion/corruptos +lock=$directorio_instalacion/lock +funciones=$directorio_instalacion/funciones +arduinos_py=$directorio_instalacion/arduinos.py +key_dir=$directorio_instalacion/llaves +private_key=$key_dir/$numero_serie.key +public_key=$key_dir/$numero_serie.key.pub + + + + + + + + + + + + + + + + diff --git a/funciones b/funciones new file mode 100644 index 0000000..26b47c0 --- /dev/null +++ b/funciones @@ -0,0 +1,270 @@ +#!/bin/bash +#set -x +get_stack () { + if [[ ! -z $1 && $1 == "wc" ]];then + ls -I "*.sig" $registros | wc -l + else + ls -I "*.sig" $registros | tail -1 + fi +} + +# Generar una llave privada ECDSA si no existe +generate_private_key () { + test -f "$private_key" && return 1 + + ssh-keygen -t ecdsa -f "$private_key" -N "" -C "$@" +} + +# Firmar el archivo usando la llave privada. +# +# Uso: sign_file archivo.json +# Devuelve: La firma +sign_file () { + local _file="$1" + + test ! -f "$_file" && return 1 + test -f "$_file.sig" || ssh-keygen -Y sign -f "$private_key" -n file "$_file" >/dev/null 2>&1 + cat "${_file}.sig" | grep -v SIGNATURE | tr -d "\n" +} + + +funcion_datos_simulados() { + + lecturas="id:dummy_plug-01 tp:temp vl:$(( $RANDOM % 20 + 15 )) un:C er:99 AD_SENSOR tp:hum vl:$(( $RANDOM % 50 + 35 )) un:percent er:99 AD_ARDUINO id:dummy_plug-02 tp:temp vl:$(( $RANDOM % 20 + 15 )) un:C er:99 AD_SENSOR tp:hum vl:$(( $RANDOM % 50 + 35 )) un:percent er:99 AD_ARDUINO id:dummy_plug-03 tp:temp vl:$(( $RANDOM % 20 + 15 )) un:C er:99 AD_SENSOR tp:hum vl:$(( $RANDOM % 50 + 35 )) un:percent er:99 AD_ARDUINO id:dummy_plug-04 tp:temp vl:$(( $RANDOM % 20 + 15 )) un:C er:99 AD_SENSOR tp:hum vl:$(( $RANDOM % 50 + 35 )) un:percent er:99 END" + +} + +funcion_datos_sensores() { + + peticiones=1 + arduinos=( 0x01 0x02 ) + for arduino_id in ${arduinos[@]};do + lectura_arduino=($(timeout 3 $arduinos_py $arduino_id)) + + let "bloques = (${#lectura_arduino[@]} - 1) / 4" + + lecturas="$lecturas id:${lectura_arduino[0]}" + + indice=1 + vueltas=1 + + while [ $vueltas -le $bloques ]; do + + lecturas="$lecturas tp:${lectura_arduino[$indice]} vl:${lectura_arduino[$(expr $indice + 1 )]} un:${lectura_arduino[$(expr $indice + 2 )]} er:${lectura_arduino[$(expr $indice + 3 )]}" + + if [ $vueltas -lt $bloques ]; then + lecturas="$lecturas AD_SENSOR" + fi + + let "indice = $indice + 4 " + + ((vueltas++)) + done + + if [ $peticiones -lt ${#arduinos[@]} ];then + lecturas="$lecturas AD_ARDUINO" + fi + ((peticiones++)) + + +done +lecturas="$lecturas END" +} + + + +funcion_configurar() { + local comando=$1 + local parametro=$2 + local parametros=$* + local url_regex='(https?|HTTPS?)://[-A-Za-z0-9\+&@#/%?=~_|!:,.;]*[-A-Za-z0-9\+&@#/%=~_|]' + local nuevo_modo='(test|sensores)' + +case $comando in + nombre) + while :;do + nuevo_nombre=$(echo $parametros | sed 's/nombre //g' | sed 's/ /_/g') + + if [[ "$nuevo_nombre" =~ ^[a-zA-Z0-9_]+$ ]];then + sed -i 's/nombre='$nombre'/nombre='$nuevo_nombre'/' $config + echo "nombre: $nuevo_nombre" + break + else + echo "El nombre solo puede contener carateres alfanumericos." + read -p "nombre: " parametro + fi + done + ;; + + servidor) + while :;do + if [[ $parametro =~ $url_regex ]]; then + sed -i 's}servidor='$servidor'}servidor='"$parametro"'}' $config + echo "Servidor $parametro" + break + else + echo "Intruzca una url válida." + read -p "url: " parametro + fi + done + ;; + + intervalo) + while :;do + if [[ $parametro -ge 1 && $parametro -le 60 ]]; then + sed -i 's/intervalo=[0-9]*/intervalo='$parametro'/' $config + echo "Intervalo seteado en $parametro" + echo "Para aplicar los cambios ejecute + sudo nodemecu captura dentener + sudo nodemecu captura iniciar" + break + else + echo "Establezca un valor entre 1 y 60." + read -p "Intervalo: " parametro + fi + done + ;; + modo) + while :;do + if [[ $parametro =~ $nuevo_modo ]]; then + sed -i 's/modo='$modo'/modo='$parametro'/' $config + break + else + echo "Los modos posibles son 'test' y 'sensores'" + read -p "modo: " parametro + + fi + + done +esac +} + + +funcion_captura() { + + + if [ "$1" = "iniciar" ]; then + + echo "Iniciando la captura de datos en modo '$modo'" + echo "" + read -p "Desea continuar? Presione 'enter' para continuar o 'ctrl-c' para cancelar..." + echo "*/$intervalo * * * * $directorio_instalacion/generador_json" > /tmp/nodemecu.crontab + crontab -u root /tmp/nodemecu.crontab + echo "" + echo "Se tomarán datos cada $intervalo minutos." + echo "" + exit + elif [ "$1" = "detener" ]; then + crontab -u root -r + echo "Se detuvo la toma de datos." + echo "" + else + echo "Ingrese una orden válida: 'iniciar' o 'detener'." + + fi +} + + +### Validaciones + +test_conf_nombre() { + + if [ -z $nombre ]; then + echo "No se establecio un nombre para el dispositivo." + return 1 + fi +} + + +test_conf_servidor () { + + if [ -z $servidor ];then + echo "No se establecio un servidor de entrega." + return 1 + fi +} + +test_conf_intervalo() { + + if [ -z $intervalo ];then + echo "No se establecio un intervalo de captura." + return 1 + fi +} + +test_conf_key() { + + if [ ! -f $private_key ];then + echo "No se creo juego de llaves." + return 1 + fi +} + + + + +funcion_validador_json() { + + validar="$@" + + echo $validar | jsonlint-php > /dev/null 2>&1 || return 1 +} + +funcion_verificar_internet() { + + if host fsf.org > /dev/null 2>&1; then + return 0 + else + return 1 + fi + +} + +funcion_espera() { + sleep 1m + #sleep ${intervalo:-1}m + +} + + +funcion_envio_registro() { + + curl -s --show-error -w "~%{http_code}" \ + -X POST -H "X-Signature: $(sign_file $registros/$file)" \ + -H "Content-Type: application/json" -d @$registros/$file \ + $servidor 2> $curl_err + +} + + +funcion_monitor() { + local sb="\x1b[" + local eb="\x1b[K\x1b[0m" + local st="\e[" + local et="\e[0m" + + while :;do + clear + echo -e "${sb}1;44;97m Nodemecu Monitor${eb}" + echo "" + echo -e "${st}1;94mNombre:${et} $nombre ${st}1;94mServidor:${et} $servidor ${st}1;94mIntervalo:${et} $intervalo" + echo "" + echo -e "${sb}1;44;97m Registros${eb}" + echo "" + echo -e "En cola: $(ls -I *.sig $registros| wc -l)" + echo -e "Enviados: $(ls -I *.sig $historicos | wc -l)" + echo -e "Registros corrupts: $(ls -I *.sig $registros_corruptos | wc -l)" + echo "" + echo -e "${sb}1;44;97m Ultimos mensajes${eb}" + echo "" + echo -e ${st}1m"Systemd unit 'nodemecu.service'${et}" + journalctl -n 3 -u nodemecu + echo "" + echo -e ${st}1m"Fichero '$log'${et}" + cat /var/log/nodemecu_errors.log | grep -v "Salida de curl" | grep -v '[*<>{}]' | tail -3 + + sleep 2 + done + +} + diff --git a/registrador b/registrador new file mode 100755 index 0000000..bddc9f3 --- /dev/null +++ b/registrador @@ -0,0 +1,80 @@ +#!/bin/bash +#set -x +source /etc/nodemecu.conf +source /opt/nodemecu/envs +source $funciones + +while :;do + + # archivo a procesar + file=$(get_stack) + + # si no hay nada que procesar reinicia el bucle + test -z $file && funcion_espera && continue + + # si no hay servidor definido se reinicia el bucle + test -z $servidor && echo "No se configuró nigun servidor de entrega." && funcion_espera && continue + + # si no hay internet reinica el bucle + if ! funcion_verificar_internet; then + err_time=$(date +%Y-%m-%d-%H:%M:%S) + echo "$err_time - Parece no haber internet." | tee -a $log + funcion_espera + continue + fi + + # si el fichero 'lock' existe (se esta creando un nuevo registro) reinicia el bucle + test -f $lock && continue + + # validación del fichero + if ! funcion_validador_json "$(cat $registros/$file)";then + mv $registros/$file $registros_corruptos + continue + fi + + # firmado de regisro + firma=$(sign_file $registros/$file) + + # UUID del registro a enviar + uuid_registro=$(jq -r '."transaction_uuid"' $registros/$file) + + # envio de registro + transaccion=$(curl -v -s --show-error -w "~%{http_code}" -X POST -H "X-Signature: $(sign_file $registros/$file)" -H "Content-Type: application/json" -d @$registros/$file $servidor 2> $curl_err) + + # respuestas del servidor + respuesta_servidor="$(echo $transaccion | cut -d '~' -f 1)" + error_servidor="$(echo $transaccion | cut -d '~' -f 2)" + + if [ ${#respuesta_servidor} -eq 36 ] && [ "$uuid_registro" == "$respuesta_servidor" ]; then + echo -e "$respuesta_servidor: \e[92mOK\e[0m" + mv $registros/$file $registros/$file.sig $historicos + else + + err_time=$(date +%Y-%m-%d-%H:%M:%S) + srv_msg="$(echo $respuesta_servidor | grep -o '.*')" + echo -e "$err_time - $uuid_registro: \e[91mFAIL\e[0m" + + echo "$err_time - $uuid_registro - Error del servidor: $error_servidor" >> $log + echo "$err_time - $uuid_registro - Mensaje del servidor:" >> $log + echo "$srv_msg" >> $log + if [ -s $curl_err ];then + echo "$err_time - $uuid_registro - Salida de curl:" >> $log + cat $curl_err >> $log + fi + + rm $curl_err + #funcion_espera + fi + + #exit +done + + + +###### + + # se testea que el servidor responda + #test_url="$(curl -s -X POST -o /dev/null -w "%{http_code}" $servidor)" + #if [ $test_url -eq 404 ];then + # echo "$(date +%Y-%m-%d-%H:%M:%S) - $url - $test_url" >> /opt/nodemecu/errors.log && sleep 300 && continue + #fi