Objectif : créer une mécanique de suivi pour un serveur Quake3
Un peu de sémantique pour commencer… nc, tee, pid, grep, pgrep, wget, mkfifo, tcpdump, inotifywait, tail, cut, rcon et surtout Quake3 !
PREMIERE ASTUCE DU SYSTEME :
SUIVRE LE FICHIER LOG POUR LES JOUEURS ET FAIRE PARLER LE NABAZTAG
1. Utilisation de inotifywait pour gérer des évènements sur un fichier (le log du jeux)
L’évènement est transmit à un tube nommé ($FIFO) déclaré au préalable qui permet de récupérer l’entrée du fichier…
FIFO="/tmp/inotify1.fifo"
FIFA="/tmp/inotify2.fifo"
if [ ! -e "$FIFO" ] ; then
mkfifo "$FIFO"
mkfifo "$FIFA"
fi
inotifywait -m -e modify "$FICHIER" > "$FIFO" & INOTIFY_PID=$!
2. Une boucle pour parcourir chaque ligne du fichier…
Le tube nommé ($FIFO) est injecté dans une première boucle, celle-ci cherche le mot ‘broadcast’ puis renvoie la ligne dans un second tube nommé ($FIFA), lui même injecté dans une seconde boucle qui permet d’appeler ‘on_event’ pour le traitement du contenu.
while read ; do
grep -E "broadcast" "$FICHIER" | uniq | tail -1 > "$FIFA" & INOTIFY_PID=$!
while read line; do
on_event "$line"
done < "$FIFA"
done < "$FIFO"
3. Choix des actions à réaliser en fonction de la ligne récupérée
Les chaines de textes sont ouvertes avec des commandes GREP, CUT pour rechercher des informations.
Une suite de IF et de CASE permettent de réaliser des actions. (Ici, utiliser l’API du Nabaztag en PHP pour annoncer la présence d’un joueur, la déconnexion…)
local message=$(echo $1 | cut -d'"' -f2)
if echo "$message" | grep -q "\^"; then
echo "LA CHAINE CONTIENT ^ (message en rapport avec un joueur)"
action=$(echo "$message" | cut -d'^' -f2 | cut -d' ' -f2 )
person=$(echo "$message" | cut -d'^' -f1 )
else
...
fi
SECONDE ASTUCE DU SYSTEME :
STOPPER/RELANCER LE SERVEUR EN FONCTIONS DES JOUEURS
1. Mettre le serveur du jeux en pause lorsqu’il n’y à pas de joueurs :
Dès que la fin de la partie est détectée depuis le fichier log du jeu : on interroge le serveur avec le protocole RCON (Remote Connexion) pour obtenir les statistiques et compter les joueurs restant :
La commande ‘echo’ envoie par le biais de nc une chaine UDP qui permet de s’authentifier et lancer une demande de status du serveur.
La chaine est retournée dans un fichier ($SCORE) dont on compte les lignes. (Impossible de faire ces deux étapes en une seule… 🙁
Si le nb est de 11, c’est qu’il ne reste que des robots dans la map du jeu.
On place le PID du serveur Quake (q3ded) en pause avec la commande kill -STOP
if [ "$action" == "match" ]; then
echo -e "\xFF\xFF\xFF\xFFrcon "PASS_DU_SERVEUR" status\n" | nc -nu IP_DU_SERVEUR 27960 | tee ${SCORES} >/dev/null 2>&1 &
nb=$(cat ${SCORES} | wc -l)
if [ "$nb" == "11" ]; then
pid=$(pgrep -x q3ded)
kill -STOP "$pid"
fi
fi
2. Relancer le serveur lors d’une demande de connexion :
Pour démarrer le serveur en cas d’arrivée d’un joueur, on utilise tcpdump pour surveiller le port de connexion et renvoyer les lignes contenant ‘getchallenge’ vers le fichier log du jeu.
La commande précédente (inotifywait) déclenche la reprise du serveur lors détection la demande de connexion.
On vérifie avant que le serveur (q3ded) ne soit pas déjà lancé.
tcpdump -A -nn -s0 -i any dst port 27960 -l 2> /dev/null | grep --line-buffered 'getchallenge' | tee -a "$FICHIER" >/dev/null 2>&1 &
if [ "$action" == "start" ]; then
etat=$(ps axc | grep q3ded | awk "{ print \$3 }")
if [ "$etat" == "T" ]; then
pid=$(pgrep -x q3ded)
kill -CONT "$pid"
fi
fi
LE SCRIPT COMPLET :
#!/bin/sh
#---------------------------------------#
# E. Grandadam - LCPROD
#
# lcprod37@gmail.com
#
# ./monitor.sh > /dev/null &
#---------------------------------------
# CONFIGURATION#---------------------------------------
FIFO="/tmp/inotify1.fifo"
FIFA="/tmp/inotify2.fifo"
FICHIER="/REPERTOIRE_DU_FICHIER_LOG/qconsole.log"
SCORES="/REPERTOIRE_DU_FICHIER_LOG/qscores.log"
# INITIALISATION#---------------------------------------
if [ ! -e "$FIFO" ] ; then
mkfifo "$FIFO"
mkfifo "$FIFA"
fi
# FONCTIONS#---------------------------------------
on_exit() {
echo "On Exit"
kill $INOTIFY_PID
killall inotifywait
rm $FIFO
rm $FIFA
exit
}
on_event() {
local message=$(echo $1 | cut -d'"' -f2)
if [ "$old" != "$message" ] && [ "$message" ] ; then
echo "message : $message"
if echo "$message" | grep -q "\^"; then
echo "LA CHAINE CONTIENT ^ (message en rapport avec un joueur)"
action=$(echo "$message" | cut -d'^' -f2 | cut -d' ' -f2 )
person=$(echo "$message" | cut -d'^' -f1 )
else
echo "LA CHAINE NE CONTIENT PAS ^ (message système)"
if echo "$message" | grep -q "getchallenge"; then
person="Serveur"
action="start"
else
person=$(echo "$message" | cut -d' ' -f1)
action=$person
fi
fi
# CAS DU ROBOT...
if echo "$message" | grep -q "Bot"; then
person="Bot $action (bot)"
action=""
fi
#CAS PARTICULIER
if echo "$message" | grep -q "renamed"; then
person=""
action="renamed"
fi
#FIN DE PARTIE
if echo "$message" | grep -q "match"; then
person=""
action="match"
fi
echo "person : $person"
echo "action : $action"
case "$person" in
"UnnamedPlayer") parle="Joueur sans nom" ;;
*) parle="Joueur inconnu : $person" ;;
esac
case "$action" in
"connectedn") parle+=" est connecté" ;;
"disconnectedn") parle+=" est déconnecté" ;;
"entered") parle+=" est dans la place" ;;
"Dropped") parle+=" est déconnecté car inactif" ;;
"fragged") parle="" ;;
"called") parle="" ;;
"renamed") parle="" ;;
"timed") parle="" ;;
"blue") parle="" ;;
"red") parle="" ;;
"Vote") parle="" ;;
*) parle="" ;;
esac
old="$message"
if [ "$action" == "match" ]; then
echo -e "\xFF\xFF\xFF\xFFrcon "PASS_DU_SERVEUR" status\n" | nc -nu IP_DU_SERVEUR 27960 | tee ${SCORES} >/dev/null 2>&1 &
rr=$(cat ${SCORES} | wc -l)
if [ "$rr" == "11" ]; then
parle="stoppé !"
pid=$(pgrep -x q3ded)
kill -STOP "$pid"
fi
fi
if [ "$action" == "start" ]; then
etat=$(ps axc | grep q3ded | awk "{ print \$3 }")
if [ "$etat" == "T" ]; then
parle="démarré !"
pid=$(pgrep -x q3ded)
kill -CONT "$pid"
fi
fi
if [ "$parle" ]; then
URL='URL_DE_L_API_DU_NABAZTAG_(Openjabnab)'
URL+=$(php -r "echo rawurlencode('$parle');")
wget -q "$URL" -O /dev/null
echo "parle : $parle"
else
echo "parle : pas !"
fi
echo "-------------------------------------"
fi
}
# ECOUTEUR
#---------------------------------------
tcpdump -A -nn -s0 -i any dst port 27960 -l 2> /dev/null | grep --line-buffered 'getchallenge' | tee -a "$FICHIER" >/dev/null 2>&1 &
inotifywait -m -e modify "$FICHIER" > "$FIFO" & INOTIFY_PID=$!
trap "on_exit" 2 3 15
# TRAITEMENT#---------------------------------------
while read ; do
#echo "Change"
grep -E "broadcast|getchallenge" "$FICHIER" | uniq | tail -1 > "$FIFA" & INOTIFY_PID=$!
while read line; do
on_event "$line"
done < "$FIFA"
done < "$FIFO"
on_exit