A la poursuite du Bash… (Bourne-again shell !)
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 !
Le fichier monitor.sh est à télécharger ici !
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 fichier monitor.sh est à télécharger ici !
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 fiif [ "$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
Comments are closed.