Utilisation d’un Mir:ror pour piloter un Mac. (LinuX / Mac)

24/02/2013
Auteur : De Emmanuel Grandadam

Comment faire revivre ce périphérique de l’âge des Nabaztag pour piloter un Mac :
Une histoire avec du C/C++, une dose de Bash et un peu d’AppleScript (osascript) et une bidouille
avec VLC

A. On commence à penser le truc sur le linuX…

L’idée : c’est de piloter le périphérique depuis un Linux mais exécuter des commandes sur d’autres machines. Pour piloter les Macs, j’utilise des scripts AppleScript lancés au travers d’un tunnel SSH.

1. Je me base sur le projet reflektor que je compile sur le linuX…
2. Ce projet écrit en C fabrique un fichier caché (.reflektor_do) dans le répertoire de l’utilisateur avec l’ID des Tags et la commande à exécuter :

     show d0021a053b453906:/home/user/mirror/actions_do.sh show d0021a053b368906 hide
     d0021a053b453906:/home/user/mirror/actions_do.sh hide d0021a053b368906 hide

3. Je monte un script Bash qui récupère les appels et obtient les actions ainsi que les ID des Tags :

     #!/bin/bash
     action=$1
     tagid=$2

     # Tag Blue
     if [ "$action" == "show" ] && [ "$tagid" == "d0021a053b368906" ]; then
          ssh user@mac "osascript -e 'tell application \"iTunes\" to play'"
     fi

Cela fonctionne parfaitement ! les appels SSH sont rapides.
Un problème tout de même : le Mir:ror reste allumé !! Je peux le désactiver dans le code C du software mais c’est fixe 🙁 et le retourner tous les soirs… c’est pas gagné…
A faire par la suite :
• Soit : Apprendre à éteindre un port USB sur le LinuX,
• Soit : Utiliser node.js pour envoyer des commandes au Mir:ror avec la bibliothèque node-hid.

B. On repense le truc mais cette fois sur le Mac…

En plaçant le système dans le mac, je pilote mieux les actions et peux couper le Mir:ror en éteignant le Mac.

1. J’utilise cette fois ci le projet hdiapi ainsi qu’une partie des modifications trouvés sur ce site .
2. Cette fois ci, le projet en C++ permet de fabriquer l’exécutable hidtest.
3. J’apporte quelques modification au fichier hidtest avant de compiler :

# buf[1] Permet d'avoir l'action
# buf[8] Permet de récupérer une une partie de l'ID du TAG pour l'identifier

if (buf[1] == 1 && buf[8] == 5) {
     printf(" IN 5");
     system("~/Library/\"Application Support\"/Mirror/actions_do.sh show blue");
}
...

4. Je garde presque le script précédent pour récupérer les appels et obtientir les actions ainsi que les ID des Tags :

#!/bin/bash
action=$1
tagname=$2

# Lancer une radio dans un VLC sans interface, récupérer le PID pour
#      pouvoir détruire l'instance lorsque le Tag est caché.
# VLC -I http  => Pas d'interface graphique, juste l'interface http.
# VLC --http-host 127.0.0.1:8085   => Permet de router le port de ce VLC du 8080 par
#      défaut vers le 8085 de manière à ne pas la voir dans l'appli Mobile.

if [ "$action" == "show" ] && [ "$tagname" == "blue" ]; then
     PROGRAM="/Application/VLC.app/Contents/MacOS/VLC -I http --http-host 127.0.0.1:8085 http://...mp3"
     $PROGRAM &
     echo $! > /tmp/vlc.pid
fi

if [ "$action" == "hide" ] && [ "$tagname" == "blue" ]; then
     PID=$(cat /tmp/vlc.pid)
     kill -15 $PID
fi

5. Je fabrique un service de lancement pour le mac avec Lingon. A placer dans Library/LaunchAgents de l’utilisateur :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
     <key>KeepAlive</key>
     <true/>
     <key>Label</key>
     <string>fr.lcprod.mirror</string>
     <key>ProgramArguments</key>
          <array>
               <string>__chemin_vers_hidtest</string>
          </array>
     <key>QueueDirectories</key>
          <array/>
     <key>RunAtLoad</key>
          <true/>
     <key>StartOnMount</key>
          <false/>
     <key>WatchPaths</key>
          <array/>
</dict>
</plist>

Cela fonctionne aussi très bien !
J’aurais tout de même aimé pouvoir récupérer à partir de hditest l’ID complet du Tag !
(Je me base sur une extraction d’un bit de la chaîne.)
Problème en C++ : Impossible de récupérer dynamiquement d’ID pour le transmettre au script Shell : Comment convertir un int en char en C++ ……….

Commentaires :

La Freebox comme disque de sauvegarde en AFP pour mon LinuX

30/01/2013
Auteur : De Emmanuel Grandadam

Utilisation du protocole AFP depuis LinuX pour accrocher la Freebox…

La recette :

  1. Utiliser le projet afpfs-ng pour avoir un client AFP sur le linux. (Basé sur Fuse)
  2. Monter un script bash pour envoyer des fichiers.
    • Attention : la commande  mount_afp est pratique sous root, mais pas sous un utilisateur. Les droits appliqués au dossier du point de montage ne sont pas définis correctement : le dossier devient invisible…
    • Je préfère utiliser afpcmd qui permet une utilisation du protocole AFP en mode interactif.
  3. #!/bin/sh
    afpcmd "afp://user:password@mafreebox.freebox.fr/Disque dur" << EOF
         cd /dossier_distant
         lcd /dossier_local
         put fichier
         quit
    EOF
  4. Le tout dans une tâche cron et le tour est joué !
Commentaires :

A la poursuite du Bash… (Bourne-again shell !)

28/10/2012
Auteur : De Emmanuel Grandadam

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
        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
Commentaires :