#!/bin/sh

# Drop-in respondd server, meant for cgi-bin

# Scraping is done by msc each hour and a custom hopglass module queries it

# Primary mac is whatever mac eth0 has. That's not the label mac but we don't care

if [ ! -z "$SERVER_PROTOCOL" ]; then
  echo -e "Content-Type: application/json\n\r"
fi

get_mac() {
  ip link show "$1" | grep 'link/ether' | awk '{ print $2 }'
}

get_intf_for_ip() {
  ip addr | awk -vtarget_addr=$1 '
/^[0-9]+/ {
  iface=substr($2, 0, length($2)-1)
}

$1 == "inet" {
  split($2, addr, "/")
  if (addr[1] == target_addr) {
    print iface
  } 
}
' | sed 's|@.*$||'
}

_arp_for_ip() {
  cat /tmp/arpcache | grep "^$1" | awk '{ print $2 }' | head -n 1
}

get_arp_for_ip() {
  mac=$(_arp_for_ip "$1")
  if [ -z "$mac" ]; then
    ping -c 1 "$1" >/dev/null
    fill_arp_cache
    mac=$(_arp_for_ip "$1")
    if [ -z "$mac" ]; then
      echo "$1"
    else
      echo "$mac"
    fi
  else
    echo "$mac"
  fi
}

MAC=$(get_mac eth0)
NODE_ID=$(echo "$MAC" | sed "s|:||g")

if [ ! -z "$QUERY_STRING" ]; then
  req="$QUERY_STRING"
elif [ ! -z "$1" ]; then
  req="$1"
fi

arp=$(cat /proc/net/arp | tail -n +2 | awk '{ print $1, tolower($4) }')

touch /tmp/arpcache

fill_arp_cache() {
  for ip in $(echo "$arp" | awk '{ print $1 }'); do
    if ! grep -q "$ip" "/tmp/arpcache"; then
      echo "$arp" | grep "$ip" >> /tmp/arpcache
    fi
  done
}

case "$req" in
  # special request so we can get the nodeid easier
  nodeid)
    echo "$NODE_ID"
    ;;
  statistics)

# Get CPU statistics from /proc/stat
raw_cpu_stats=$(grep '^cpu ' /proc/stat)

# Extract individual values
user=$(echo "${raw_cpu_stats}" | awk '{print $2}')
nice=$(echo "${raw_cpu_stats}" | awk '{print $3}')
system=$(echo "${raw_cpu_stats}" | awk '{print $4}')
idle=$(echo "${raw_cpu_stats}" | awk '{print $5}')
iowait=$(echo "${raw_cpu_stats}" | awk '{print $6}')
irq=$(echo "${raw_cpu_stats}" | awk '{print $7}')
softirq=$(echo "${raw_cpu_stats}" | awk '{print $8}')

if [ -z "$iowait" ]; then
  iowait="0"
fi

if [ -z "$irq" ]; then
  irq="0"
fi

if [ -z "$softirq" ]; then
  softirq="0"
fi

# Output as JSON
cpu_stats="{\"user\":${user},\"nice\":${nice},\"system\":${system},\"idle\":${idle},\"iowait\":${iowait},\"irq\":${irq},\"softirq\":${softirq}}"

# Get memory statistics from /proc/meminfo
raw_mem_stats=$(grep -E '^MemTotal:|^MemFree:|^MemAvailable:|^Buffers:|^Cached:' /proc/meminfo)

# Extract individual values
total=$(echo "${raw_mem_stats}" | grep '^MemTotal:' | awk '{print $2}')
free=$(echo "${raw_mem_stats}" | grep '^MemFree:' | awk '{print $2}')
available=$(echo "${raw_mem_stats}" | grep '^MemAvailable:' | awk '{print $2}')
buffers=$(echo "${raw_mem_stats}" | grep '^Buffers:' | awk '{print $2}')
cached=$(echo "${raw_mem_stats}" | grep '^Cached:' | awk '{print $2}')

if [ -z "$available" ]; then
  available="$free"
fi

# Output as JSON
memory_stats="{\"total\":${total},\"free\":${free},\"available\":${available},\"buffers\":${buffers},\"cached\":${cached}}"


# Get uptime and idletime from /proc/uptime
uptime_and_idletime=$(cat /proc/uptime)
uptime=$(echo "${uptime_and_idletime}" | awk '{print $1}')
idletime=$(echo "${uptime_and_idletime}" | awk '{print $2}')

# Get load average from /proc/loadavg
loadavg=$(cat /proc/loadavg | awk '{print $1}')

# Get the number of running and total processes
running_processes=$(ps | grep -c ' R')
total_processes=$(ps | wc -l)

# Output as JSON
metrics="\"uptime\":${uptime},\"idletime\":${idletime},\"loadavg\":${loadavg},\"processes\":{\"running\":${running_processes},\"total\":${total_processes}}"

# Get CPU statistics from /proc/stat
raw_cpu_stats=$(grep -E '^intr |^ctxt |^processes |^softirq ' /proc/stat)

# Extract individual values
intr=$(echo "${raw_cpu_stats}" | grep '^intr ' | awk '{print $2}')
ctxt=$(echo "${raw_cpu_stats}" | grep '^ctxt ' | awk '{print $2}')
processes=$(echo "${raw_cpu_stats}" | grep '^processes ' | awk '{print $2}')
softirq=$(echo "${raw_cpu_stats}" | grep '^softirq ' | awk '{print $2}')

if [ -z "$softirq" ]; then
  softirq="0"
fi

# Output as JSON
cpu_extra="\"intr\":${intr},\"ctxt\":${ctxt},\"processes\":${processes},\"softirq\":${softirq}"

# Get the current Unix timestamp
current_time=$(date +%s)

# Get the root filesystem usage
rootfs_total=$(df | grep " /$" | awk '{ print $2 }')
rootfs_used=$(df | grep " /$" | awk '{ print $3 }')
rootfs_usage=$(( $(( $rootfs_used * 100 )) / $(( $rootfs_total )) ))

if [ "$(echo -n "$rootfs_usage" | wc -c | awk '{ print $1 }')" != "3" ]; then
  if [ "$(echo -n "$rootfs_usage" | wc -c | awk '{ print $1 }')" != "2" ]; then # 1
    rootfs_usage="0.0$rootfs_usage"
  else # 2
    rootfs_usage="0.$rootfs_usage"
  fi
else # 3
  rootfs_usage=$(echo "$rootfs_usage" | sed "s|^1|1.|")
fi

# Output as JSON
metrics2="\"time\":${current_time},\"rootfs_usage\":${rootfs_usage}"

    echo '{"clients":{"total":0,"wifi":0,"wifi24":0,"wifi5":0,"owe":0,"owe24":0,"owe5":0},"traffic":{"rx":{"bytes":0,"packets":0,"dropped":0,"errors":0},"tx":{"bytes":0,"packets":0,"dropped":0,"errors":0}},"node_id":"'"$NODE_ID"'",'"$metrics2"',"memory":'"$memory_stats"',"stat":{"cpu":'"$cpu_stats"','"$cpu_extra"'},'"$metrics"'}'
    ;;
  nodeinfo)

model="unknown"
firmware="{}"

# if [ -e /etc/freifunk-release ]; then
#   firmware="{\"base\":\"ffgraz-old\",\"release\":\"$(cat /etc/freifunk-release | head -n 1)\"}"
# fi

if [ -e /tmp/board.json ]; then
  model=$(cat /tmp/board.json | grep "name" | grep -o ": \".*\"$" | grep -o "[^\"]*\"" | tail -n 1 | grep -o "[^\"]*")
fi

if [ -e /usr/sbin/nvram ]; then
  model="$(nvram get boardtype) $(nvram get boardnum)"
fi

if [ -e /etc/os-release ]; then
  source /etc/os-release
  firmware="{\"base\":\"$OPENWRT_RELEASE\",\"release\":\"$VERSION\"}"
fi

ips=$(ip a | grep "inet" | sed "s|    inet6* ||g" | sed "s|/.*||g" | grep -E "(^fdff:|10.12.)")
hostname=$(cat /proc/sys/kernel/hostname)
num_cores=$(grep -c '^processor' /proc/cpuinfo)

openvpn=false
if ps | grep openvpn | grep -v grep | grep -q openvpn; then
  openvpn=true
fi

olsrd=false
if ps | grep olsrd | grep -v grep | grep -q olsrd; then
  olsrd=true
fi

olsrd2=false
if ps | grep oonf- | grep -v grep | grep -q oonf-; then
  olsrd2=true
fi

wifi=$(cat /proc/net/wireless | tail -n +3 | cut -d":" -f1 | awk '{ print $1 }')
bridge=$(brctl show | tail -n +2 | awk '{if ($4 == "") { print $1 } else { print $4 } }')
bridge_intf=$(brctl show | tail -n +2 | awk '{if ($4 == "") { print intf, $1 } else { print $4; intf = $4; } }')
vlan=$(cat /proc/net/vlan/config 2>/dev/null | tail -n +3 | awk '{ print $1, $5 }')
is_wireless() {
  echo "$wifi" | grep -q "^$1$"
}

add_intf() {
  if is_wireless "$intf"; then
    list_wireless="$list_wireless\"$(get_mac "$intf")\""
  else
    list_wired="$list_wired\"$(get_mac "$intf")\","
  fi
}

recurse_intf() {
  # add self
  add_intf "$1"

  for b in $(echo "$bridge_intf" | grep "^$1 " | awk '{ print $2 }'); do
    recurse_intf "$b"
  done

  for v in $(echo "$vlan" | grep "^$1 " | awk '{ print $2 }'); do
    recurse_intf "$v"
  done
}

interfaces=""
intfs=$(cat /proc/net/dev | tail -n +3 | cut -d":" -f1 | awk '{ print $1 }' | grep -v '^lo$')

for intf in $intfs; do
  list_wireless=""
  list_wired=""
  list_tunnel=""

  if echo "$bridge" | grep -q "$intf" || echo "$vlan" | grep -q "$intf\$"; then
    # this is not an independent interface, but rather part of a bridge or just a vlan interface - skip it
    :
  else
    recurse_intf "$intf"
    # Remove the trailing comma
    list_wired=${list_wired%,}
    list_wireless=${list_wireless%,}
    interfaces="$interfaces\"$intf\":{\"interfaces\":{\"wireless\":[$list_wireless],\"wired\":[$list_wired]}},"
  fi
done

# Remove the trailing comma
interfaces=${interfaces%,}

addresses=""
addresses_olsr=""
addresses_olsr2=""

for ip in $ips; do
  addresses="$addresses\"$ip\","
  if echo "$ip" | grep -q "^f"; then
    addresses_olsr2="$addresses_olsr2\"$ip\","
  else
    addresses_olsr="$addresses_olsr\"$ip\","
  fi
done

addresses=${addresses%,}
addresses_olsr=${addresses_olsr%,}
addresses_olsr2=${addresses_olsr2%,}

    echo '{"software":{"autoupdater":{"enabled":false},"olsr1":{"running":'"$olsrd"',"addresses":['"$addresses_olsr"']},"olsr2":{"running":'"$olsrd2"',"addresses":['"$addresses_olsr2"']},"firmware":'"$firmware"'},"network":{"addresses":['"$addresses"'],"mesh":{'"$interfaces"'},"mesh_vpn":{"bandwidth_limit":{"enabled":false},"provider":"openvpn","enabled":'"$openvpn"'},"mac":"'"$MAC"'"},"system":{"site_code":"ffgraz-external"},"node_id":"'"$NODE_ID"'","hostname":"'"$hostname"'","hardware":{"model":"'"$model"'","nproc":'"$num_cores"'}}
'
    ;;
  neighbours)

# local, remote, tq, etc
olsr_neigh=$(wget -q -O - http://127.0.0.1:2006/links | tail -n +3 | awk '{ if ($1 != "" && $5 != "INFINITE") { print $1, $2, $3 * $4 * 255, 1 / ($3 * $4) } }' | sort)
if [ -z "$olsr_neigh" ]; then
  olsr_neigh=$(wget -q -O - http://127.0.0.1:2006/lin | tail -n +3 | awk '{ if ($1 != "" && $6 != "INFINITE") { print $1, $2, $4 * $6 * 255, 1 / ($4 * $5) } }' | sort)
fi

neighs=""

last_intf=""

IFS="
"
for neigh in $olsr_neigh; do
  intf=$(get_intf_for_ip "$(echo "$neigh" | awk '{ print $1 }')")
  if [ "$last_intf" != "$intf" ]; then
    if [ ! -z "$last_intf" ]; then
      neigh_list=${neigh_list%,}
      neighs="$neighs\"$(get_mac "$last_intf")\":{\"neighbours\":{$neigh_list}},"
    fi

    neigh_list=""
    last_intf="$intf"
  fi

  neigh_ip=$(echo "$neigh" | awk '{ print $2 }')
  tq=$(echo "$neigh" | awk '{ print $3 }')
  etx=$(echo "$neigh" | awk '{ print $4 }')

  neigh_list="$neigh_list\"$(get_arp_for_ip "$neigh_ip")\":{\"ifname\":\"$intf\",\"best\":false,\"etx\":$etx,\"tq\":$tq,\"olsr1_ip\":\"$neigh_ip\"},"
done
IFS=" "

if [ ! -z "$last_intf" ]; then
  neigh_list=${neigh_list%,}
  neighs="$neighs\"$(get_mac "$last_intf")\":{\"neighbours\":{$neigh_list}},"
fi


neighs=${neighs%,}

# TODO: wifi "36:80:75:a4:e8:71":{"neighbours":{"1a:30:97:b2:a9:85":{"signal":-45,"noise":-95,"inactive":40},"18:fd:74:77:27:6a":{"signal":-54,"noise":-95,"inactive":40}}}

# TODO: olsr2

  echo '{"batadv":{'"$neighs"'},"node_id":"'"$NODE_ID"'","wifi":{}}'

    ;;

  *)
    echo "{\"404\":true,\"node_id\":\"$NODE_ID\"}"
    ;;
esac
