Archives de Catégorie: Linux

Shell : Avoir des options en plus des arguments

Rien d’innovant, mais une syntaxe comme celle là il faut l’avoir à portée de main 😉
Quelques contraintes que je ne retrouve pas dans d’autres langages : Les options doivent être passées avant les arguments.
ce bout de code permet d’analyser les options (ici -u qui attend un argument). Le reste des arguments ($1, $2,…) est à récupérer normalement, les options sont supprimées de la ligne d’appel après lecture (et donc ne décalent pas la numérotation des arguments).

#!/bin/ksh

uid=""
while getopts u: option
do
    case $option in
        u) uid="${OPTARG}" ;;
        🙂 echo "option -$OPTARG needs an argument" ;;
        [?]) echo "Usage incorrect : "
                echo "$0 [-u option] arg1 "
                exit 1
                ;;
    esac
done
shift $OPTIND-1

echo "uid : [$uid]"
echo "\$@ : $@"
echo "\$# : $#"

echo "\$1 : $1"

Sinon il y a la méthode complète : http://docstore.mik.ua/orelly/unix3/korn/appb_11.htm

Publicités

Un système audio pour la maison [Part 2]

Dans le précédent article je vous ai décris (à la limite de la pub) ce que pouvait faire une squeezebox « du commerce »

Maintenant je vais vous parler de se faire sa propre squeezebox radio à base de Raspberry Pi.

Si on considère qu’on a déjà un Raspberry Pi d’installé l’opération est très simpe. Un tutoriel complet est décrit dans cet article, mais personnellement j’ai sabré bon nombre d’étapes (Wifi,..)

Dans les faits j’ai eu quelques problemes avec la distrib que j’avais installé à la base (la débian de base du Pi ? je ne sais plus), le son était mauvais. Des « clicks » se faisaient entendre. Vu que je n’avais pas grande idée sur le sujet j’ai utilisé la Raspifi que j’avais installé précédemment et appliqué la procédure ci dessus. Cette fois les « clicks » n’étaient pas présents.

A l’heure actuelle je fonctionne avec la Raspify, mais sans utiliser son interface. Il n’y a pas de conflit entre squeezelite et mpd donc je garde pour le moment 😉

Les étapes importantes à garder sont les suivante :

sudo apt-get update
sudo apt-get install libfaad2 libmad0

cd /usr/bin/
sudo wget http://squeezelite.googlecode.com/files/squeezelite-armv6
sudo chmod u+x squeezelite-armv6

A partir de ce moment là il est possible de lancer squeezelite-armv6 et de profiter du son (pour peu qu’on ai un serveur installé comme décrit dans la partie 1 … faut suivre 🙂 )

Pour lancer automatiquement le client j’utilise le script suivant, installé dans /etc/init.d/squeezelite-armv6 (j’ai pris comme base celui fourni avec le tuto cité précédemment ).

#! /bin/sh
### BEGIN INIT INFO
# Provides: squeezelite
# Required-Start:
# Required-Stop:
# Should-Start:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Squeezeslitee
# Description: Light weight streaming audio player for Logitech's Squeezebox audio server
### END INIT INFO</code>

# Author: Me
#
# Install Instructions
#
# Copy file to /etc/init.d/squeezeslite
# chmod 755 /etc/init.d/squeezeslite
# update-rc.d squeezeslitee defaults
#
# Create /etc/default/squeezeslite to override any default
# variables defined here. No not edit this file.
#
# Uninstall Instructions
#
# update-rc.d squeezeslite remove
#

# Do NOT "set -e"

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Squeezebox client"
NAME=squeezelite-armv6
DAEMON=/usr/bin/$NAME
SCRIPTNAME=/etc/init.d/$NAME
SLOPTIONS="-a 120 -o default:CARD=ALSA"
SLLOG=/var/log/squeezeslite.log
# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] &amp;&amp; . /etc/default/$NAME

DAEMON_ARGS="${SLOPTIONS}"

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Define LSB log_* functions.
# Depend on lsb-base (&gt;= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions

#
# Function that starts the daemon/service
#
do_start()
{
    if [ -f ${SLLOG} ]; then
    rm ${SLLOG}
    fi
    start-stop-daemon --start --quiet --background --exec $DAEMON -- $DAEMON_ARGS || return 2
}

#
# Function that stops the daemon/service
#
do_stop()
{
    killall $DAEMON
    RETVAL="$?"
    [ "$RETVAL" = 2 ] &amp;&amp; return 2
}

# MAIN #
case "$1" in
start)
    [ "$VERBOSE" != no ] &amp;&amp; log_daemon_msg "Starting $DESC" "$NAME"
    do_start
    case "$?" in
        0|1) [ "$VERBOSE" != no ] &amp;&amp; log_end_msg 0 ;;
        2) [ "$VERBOSE" != no ] &amp;&amp; log_end_msg 1 ;;
    esac
    ;;
stop)
    [ "$VERBOSE" != no ] &amp;&amp; log_daemon_msg "Stopping $DESC" "$NAME"
    do_stop
    case "$?" in
        0|1) [ "$VERBOSE" != no ] &amp;&amp; log_end_msg 0 ;;
        2) [ "$VERBOSE" != no ] &amp;&amp; log_end_msg 1 ;;
    esac
    ;;
    status)
        status_of_proc "$DAEMON" "$NAME" &amp;&amp; exit 0 || exit $?
    ;;
    restart)
        log_daemon_msg "Restarting $DESC" "$NAME"
        do_stop
        case "$?" in
            0|1)
                do_start
                case "$?" in
                    0) log_end_msg 0 ;;
                    1) log_end_msg 1 ;; # Old process is still running
                    *) log_end_msg 1 ;; # Failed to start
                esac
                ;;
            *)
                # Failed to stop
                log_end_msg 1
                ;;
            esac
        ;;
        *)
            echo "Usage: $0 {start|stop|status|restart|force-reload}" &gt;&amp;2
            exit 3
        ;;
esac

N’oubliez pas de le rendre exécutable avant de faire un service squeezelite-armv6 start
Un update-rc.d squeezelite-armv6 defaults plus loin vous pouvez rebooter, il redémarrera tout seul 😉

C’est un peu brut de décoffrage , mais ça fonctionne … à suivre pour quelques raffinements

Un système audio pour la maison [Part 1]

Suite à un article trouvé chez Korben j’ai découvert la distribution Raspifi qui a pour but de lire ses mp3 à l’aide d’un rasperry pi.

La distrib s’installe sans trop de problème et j’ai pu lire les mp3 qui sont sur mon NAS.

N’ayant pas été convaincu par l’interface web de l’engin j’ai été aiguillé vers la solution de Logitech.

Bon disons le tout de suite, c’est une solution  propriétaire, donc qui pourrait disparaître à tout moment. Mais j’ai rarement vu une interface propriétaire qui autorise autant de possibilité, on est plutôt dans l’ère de la Pomme avec un simple bouton Play (pour les autres c’est en option 😉 ).

Cette solution réside sur une solution client/serveur et impose donc de laisser tourner un PC tout le temps. Vu que c’est déjà le cas pour moi ça ne me dérange pas ;). Il permet de lire des mp3, mais aussi des web radios (ce qui rejoint un de mes précédents articles).

Le serveur

De son petit nom « Logitech Media Server », il se télécharge sur le site du fabriquant de souris, et s’installe sous Linux (DEB ou RPM), Mac, Windows et quelques NAS. Une fois installé il faut s’enregistrer sur le site de logitech, cela permettrait théoriquement de contrôler les squezebox depuis internet. Dans les faits je n’ai jamais réussi à partir de ce site, mais ce n’est pas grave 😉 . Ensuite on lui faire indexer ses mp3, c’est assez classique. Il y a possibilité d’ajouter des plugins, des interrogations vers last.fm ou d’autre, … les possibilité sont assez étendues.

En l’état le système ne fonctionne pas, on a pas la possibilité de lire de mp3, pour cela il faut un (ou plusieurs clients)

Le client (payant)

Logitech ne faisant pas ça gratuitement compte quand même vendre du matos,en l’occurrence il s’agit des Squeezebox Radio. A priori la gamme était plus vaste il y a quelques années mais la marque a recentré sa stratégie 😦

J’ai donc acheté une Squeezebox Radio pour la cuisine. La petite bête est sympathique avec son Wifi et du son mono de bonne qualité. Disponible aux alentours de 130€ ça peut refroidir, mais je ne regrette pas du tout mon achat.

L’ergonomie de la petite bête est très agréable et dispose d’un excellent WAF ce qui fait qu’elle a été adoptée par madame de façon surprenante.

Le client (gratuit)

Là où la chose est beaucoup plus intéressante, c’est qu’il est possible d’installer squeezelite sur son ordinateur (ou sur un raspberry pi, mais j’y viendrais plus tard). à partir de ce moment là il est possible de streamer de la musique sur l’ordinateur depuis le serveur.

Et il peut y avoir plusieurs clients en même temps. Par exemple un serveur familiale avec la musique et des raspberry dans les autres pièces  raccordés aux enceintes.

D’ailleurs cette multitude de client a un fonctionnalité que je n’avais jamais vu avant c’est la « synchonisation » des clients. Par un menu il est possible d’indiquer que deux clients vont diffuser les mêmes programmes, idéal quand on se déplace dans la maison, une sorte de radio d’intérieure en somme.

Les interfaces

Afin de piloter la lecture il y a aussi foultitude de possibilité, on peu se connecter sur le site web de logitech (même si ça ne marche pas chez moi), on peu se connecter au serveur (et choisir sur quel client la lecture se passera). Il existe aussi des interfaces pour Android (même topo), la squeezebox radio est aussi autonome de ce coté là (mais peut aussi être pilotée par le réseau. Enfin il est aussi possible de piloter via le protocole réseau qui est disponible sur le Net (j’en reparlerais plus tard).

Et ceci n’est que le commencement … à suivre

Chasser les pertes de mémoire en C sous Linux

Pour un fois je vais pouvoir parler de ce que je fais pour le boulot. En effet, j’ai planché sur un sujet qui peut servir à autrui.
Un des programmes écrit par un de mes prédécesseur avait une fâcheuse tendance à prendre de plus en plus de mémoire jusqu’à bloquer la machine (tssss un process lancé en root).

Chasser les fuites mémoire est toujours un peu hasardeux, il suffit de peu pour que ça devienne mission impossible.

J’ai travaillé principalement avec deux « outils » : valgrind et /proc

L’approche valgrind

valgrind est un utilitaire dédié à l’inspection de la mémoire d’un programme. Il note toutes les zones allouées et vérifie si elles sont correctement désallouées a la fin de celui-ci.

Il existe de très bons tuto sur le net pour son utilisation, par exemple celui-ci : http://fr.openclassrooms.com/informatique/cours/debuguer-facilement-avec-valgrind

L’approche /proc/*/status

Bon ce n’est pas vraiment une méthode très académique, mais elle permet de se rendre compte précisément de la consommation mémoire en temps réel de son programme.

Le noyau Linux met a disposition de l’utilisateur un grand nombre d’information sur les processus en cours d’exécution.

Beaucoup de ses informations sont disponibles dans le point de montage /proc.

En ce qui nous concerne, chaque processus, connu par son pid met a disposition les informations sur sa conso mémoire dans le fichier /proc/<pid du processus>/status.

 $ cat /proc/523/status
 Name: firefox
 State: S (sleeping)
 Tgid: 523
 Pid: 523
 PPid: 1
 TracerPid: 0
 Uid: 1000 1000 1000 1000
 Gid: 1000 1000 1000 1000
 FDSize: 128
 Groups: 4 20 24 25 29 30 33 44 46 103 104 114 115 126 146 147 148 1000 1004 1010
 VmPeak: 1259364 kB
 VmSize: 1164072 kB
 VmLck: 0 kB
 VmPin: 0 kB
 VmHWM: 412984 kB
 VmRSS: 365880 kB
 VmData: 622216 kB
 VmStk: 200 kB
 VmExe: 92 kB
 VmLib: 116232 kB
 VmPTE: 1784 kB
 VmSwap: 0 kB
 Threads: 34
 SigQ: 0/95953
 SigPnd: 0000000000000000
 ShdPnd: 0000000000000000
 SigBlk: 0000000000000000
 SigIgn: 0000000000001000
 SigCgt: 0000000f800144af
 CapInh: 0000000000000000
 CapPrm: 0000000000000000
 CapEff: 0000000000000000
 CapBnd: ffffffffffffffff
 Cpus_allowed: f
 Cpus_allowed_list: 0-3
 Mems_allowed: 00000000,00000001
 Mems_allowed_list: 0
 voluntary_ctxt_switches: 3852953
 nonvoluntary_ctxt_switches: 874330
 

La zone qui m’intéresse dans le cas d’une zone mémoire c’est la ligne VmRSS (qui vaut ici 365880 kB).
Celle-ci reflete la quantité de mémoire alloué actuellement par le processus. Attention à la valeur des autres zones, certaines comme VmSize comptabilisent aussi les librairies partagées et les fichiers ouverts (même s’il ne sont pas chargés en mémoire !), il ne s’agit donc pas forcement de mémoire utilisée !

Grace à la lecture de ce pseudo fichier il est possible de vérifier en cours d’utilisation la mémoire consommée et de tracer son évolution à partir du programme.

En somme Valgrind permet de chasser la mémoire utilisée et /proc de vérifier qu’il n’y a pas de cas tordus qui pourraient passer à travers.

Pour automatiser la chose

un petit script

pid=`ps -fe | grep [nom du process] |grep -v grep | awk '{print $2}'` && cat /proc/$pid//status |grep Vm

une petite fonction

Vu qu’on me l’a demandé j’ai aussi une petite fonction (très fortement inspirée de celle-ci ) pour afficher dans la log l’état de la mémoire (en même temps que les éléments clef du programme)

int memStats()
{
  char * line;
  char *vmsize;
  char *vmdata;
  char *vmrss;
  char *vmstk;

  size_t len;

  FILE *f;
 
  vmsize = NULL;
  vmdata = NULL;
  vmrss = NULL;
  vmstk = NULL;
  len = 128;
  line = malloc ( len * sizeof( char ) ) ;

  char * path2status = malloc( 128 * sizeof( char ) );
  sprintf (path2status, "/proc/%d/status", getpid() );

  f = fopen(path2status, "r");
  if (!f)
  {
    free ( path2status );
    free ( line );
    return 1;
  }
  free ( path2status );
  /* Read memory size data from /proc/pid/status */
  while (!vmsize || !vmrss || !vmdata || !vmstk )
  {
    if ( fgets ( line, len, f ) == NULL )
    {
      /* Some of the information isn't there, die */
      free(line);
      fclose( f ) ;
      return 1;
    }

    /* Find VmSize */
    else if (!strncmp(line, "VmSize:", 7))
    {
      vmsize = strdup(&line[7]);
    }

    /* Find VmRSS */
    else if (!strncmp(line, "VmRSS:", 6))
    {
      vmrss = strdup(&line[7]);
    }

    /* Find VmData */
    else if (!strncmp(line, "VmData:", 7))
    {
      vmdata = strdup(&line[7]);
    }
    /* Find VmStk */
    else if (!strncmp(line, "VmStk:", 6))
    {
      vmstk = strdup(&line[7]);
    }

  }
  free(line);

  fclose(f);

  /* Get rid of " kB\n"*/
  len = strlen(vmsize);
  vmsize[len - 4] = 0;
  len = strlen(vmstk);
  vmstk[len - 4] = 0;
  len = strlen(vmrss);
  vmrss[len - 4] = 0;
  len = strlen(vmdata);
  vmdata[len - 4] = 0;

  /* Output results to stderr */
  fprintf( stderr, "<%s;%s;%s;%s>\n", vmrss, vmsize, vmstk, vmdata);

  free(vmstk);
  free(vmsize);
  free(vmrss);
  free(vmdata);
  return 0;
}

Bonne chasse …

Mettre de la couleur dans vos shell en perl

En balayant mes archives je tombe sur une librairie que j’avais écrite il y a quelques années.

Elle permet de changer « simplement » les attributs d’un texte écrit par un script shell Unix (ou Linux).
La librairie propose 4 fonctions :

  • color_fg_set_str , modifie la couleur d’écriture du texte
  • color_bg_set_str , modifié la couleur de fond d’écran du texte
  • curs_pos_str , déplace le curseur
  • decoration_set_str , bascule en gras, souligné, clignotant, …

use constant ESC_FMT		=> "\033[%sm";
use constant ESC_FMT1	=> "\033[%dm";
use constant ESC_FMT2	=> "\033[%d;%dm";
use constant ESC_FMT3	=> "\033[%d;%d;%dm";
#use constant CLR_SCR		=> "\f";
use constant CLR_SCR		=> "\033[2J";
use constant CLR_EOL		=> "\033[K";
use constant POS_CUR		=> "\033[%d;%dH";
use constant CUR_SAV		=> "\033[s";
use constant CUR_RES		=> "\033[u";
use constant FLUSH		=> CUR_SAV . "\n" . CUR_RES;
use constant ESC_FMTC1	=> "\033[%d%s";
use constant ESC_FMTC2	=> "\033[%d;%d%s";
use constant ESC_DEFAULT	=> "\033[0m\033[37;40m";

#	DECORATIONS
use constant OFF	  	=>  0;
use constant BOLD		  =>  1;
use constant UNDER	  =>  4;
use constant BLINK	  =>  5;
use constant REVERSE	=>  7;
use constant CONCEAL  =>  8;

#	COULEURS
use constant BLACK		=>  30;
use constant RED			=>  31;
use constant GREEN		=>  32;
use constant YELLOW		=>  33;
use constant BLUE			=>  34;
use constant MAGENTA	=>  35;
use constant CYAN			=>  36;
use constant WHITE		=>  37;

sub color_fg_set_str
{
	my $color = shift;	#couleur désiré
	$color = $default_fg_color if (! $color);
	if ($color >= BLACK && $color <= WHITE)	# couleur connue
	{
		return sprintf ESC_FMT1, $color;				# changement de couleur
	}
	return "";
}

sub curs_pos_str
{
	my ($lig, $col)= ( @_ );

	if ( $lig > 0 and $col > 0 )
	{ return sprintf(POS_CUR, $lig, $col); }
	else
	{	return ""; }
}

sub color_bg_set_str
{
	my $color = shift;	#couleur désiré
	$color = $default_bg_color if (! $color);
	if ( ( $color>=BLACK && $color<=WHITE ) )	# couleur connue
	{
		return sprintf ESC_FMT1, ($color + 10);			# changement de couleur
	}
	return "";
}

sub decoration_set_str
{
	my $decoration = shift;	#decoration désiré
	$decoration = $default_decoration if (! $decoration );
	if ( ( $decoration>=OFF && $decoration<=CONCEAL ) )	# decoration connue
	{
		return sprintf ESC_FMT1, ($decoration);			# changement de decoration
	}
	return "";
}


En espérant que cela serve à quelqu’un un jour

PS : Une mine d’info https://wiki.archlinux.org/index.php/Color_Bash_Prompt

Problème d’accents circonflexes avec Ubuntu 12.04

Je viens de mettre à jour (pour le meilleur et pour le pire surtout) ubuntu en 12.04.

Outre une version de Unity que je peine à paramétrer (ne marchons pas dans le troll …) j’avais un problème avec la saisie d’accents.

En effet jusqu’à présent lorsque je voulais saisir un ‘ê’ j’appuyais sur ‘^’ puis sur ‘e’. Même fonctionnement pour ï ou î.

Or depuis cette (satanée) migration lorsque je presse ‘^’ puis ‘e’ ça affiche  ‘^’ puis ‘e’ 😦

 

Apres pas mal de tests infructueux ( j’ai suivi ce fil là  qui donne beaucoup de bons conseils), j’ai trouvé la solution qui fonctionne (chez moi du moins …).

 

dans un terminal tapez ‘im-switch’, dans la fenetre saisisez ‘Do not use Input Methode. (none)’, redemarrez la session et … ça remarche !

Input Method Switcher

 

 

Ouf …

Nautilus et les emblèmes

Sous Gnome quand j’utilise Nautilus je note les documents que j’ai lu à l’aide d’une petite coche, on appelle ça les emblèmes.

Or sous Nautilus 3, qui arrive avec les dernières versions d’Ubuntu, le sélecteur d’emblème a disparu.

En cherchant j’ai trouvé des solutions avec des programmes tiers, mais mon besoin est beaucoup plus simple. Je souhaite juste basculer une emblème donnée sur une liste de fichier (et pouvoir l’en retirer), rien de plus.

J’ai donc écris deux scripts shell, l’un pour ajouter un emblème, l’autre pour les retirer tous. L’emplacement de stockage importe peu, nautilus-action fera le lien entre le menu et les scripts. Pensez quand même à les rendre exécutable.

Ces scripts auront besoin de lineakd pour forcer le rafraîchissement automatique de Nautilus après avoir fait l’association de l’emblème (cette partie me plait moyennement, mais pas trouvé mieux).

sudo apt-get install lineakd

setAsRead.sh

#!/bin/sh
while [ ! -z $1 ]
do
gvfs-set-attribute -t stringv "$1" metadata::emblems default
shift
done
# Nécessite l'installation de lineakd
xsendkeycode 71 1
xsendkeycode 71 0

setAsUnread.sh

#!/bin/sh
while [ ! -z $1 ]
do
gvfs-set-attribute -t unset "$1" metadata::emblems
shift
done
# Nécessite l'installation de lineakd
xsendkeycode 71 1
xsendkeycode 71 0

Nautilus Actions Configuration Tool

Cet outil permet d’ajouter des éléments dans le menu contextuel de Nautilus. Si vous ne l’avez pas d’installé, un petit apt-get install nautilus-action devrait faire l’affaire.

Paramétrage de Nautilus Action pour le script setAsRead

Paramétrage de Nautilus Action pour le script setAsRead

EDIT : Le paramètre a utiliser est « %U », j’utilisais abusivement « %F », or ce dernier ne fonctionne qu’avec des noms de fichiers locaux, donc pas de modification d’emblème de fichiers accédés par Samba

Et voila le résultat, lors d’un clic droit sur mon fichier j’ai deux nouveaux éléments : Set As Read, et Set As Unread.

Cette solution ne permet pas de choisir l’emblème, mais ce n’était pas mon propos.

De plus , la gestion du rafraîchissement de Nautilus laisse a désirer, la solution employée ici consiste à émuler un rafraîchissement de page comme on le ferait avec F5. Tant que je suis en local avec de petits répertoire il n’y aura pas de problème, mais pour peu que le réseau soit un peu lent que qu’il y ait beaucoup de fichiers cela pourrait être pénalisant. En ce cas il faudra probablement passer par une solution écrite en C utilisant l’API de Nautilus, il est fort probable qu’il soit possible de communiquer avec ce dernier pour lui faire rafraîchir le seul emblème du fichier modifié, et non relire tout le répertoire.

 

 

 

EDIT 15/11/2012 : Apres une mise à jour vers la version 12.04 (Precise Pangolin), le script reste valide mais lineakd n’existe plus, le rafraichissement automatique n’est plus actif :(.
Un coup de F5 pour mettre à jour est donc nécessaire, pour l’instant en attendant que je trouve une parade.

Récupérer des données sur un disque RAID 1 issu d’un synology

Suite à une boulette de ma part mon Synology est HS (ne jamais faire un reboot apres un echec dans la mise à jour de DSM).

Comme j’avais tout un tas de données dessus j’ai cherché à en faire un backup.

Pour ce faire j’ai extrait l’un des disques dur du Synology (Ils sont en RAID 1 – mirroring) et l’ai copié sur un autre disque vierge pour travailler tranquillement . [ merci http://fredo.servehttp.com/html/Astu-19_3.htm]. Les opérations seront faites à partir d’une machine Linux (Ubuntu)

dd if=/dev/sdc of=/dev/sdb bs=64k conv=noerror

J’installe mdadm pour avoir accès au RAID

J’ai rebooté en ayant le seul disque de copie (le disque original est à l’abri), mais le raid ne veux pas démarrer (erreur au démarrage). Je poursuis donc le boot et tente les commandes en manuel [merci http://forum.synology.com/wiki/index.php/How_to_retrieve_data_from_RAID_Volumes_on_Linux] :

mdadm --assemble /dev/md2 /dev/sdb3

Mais le système se plaint de ne pas trouver de superblock.

Pour information un disque synology est découpé en plusieurs partitions :

sdb1 : partition root linux

sdb2 : partition SWAP

sdb3 : partition étendue

sdb5 : partition de données

Tout du moins c’est ce que je croyais avoir compris, d’où un doute sur l’utilisation de sdb3 au lieu de sdb5 (la commande ne donne pas grand chose de mieux avec sdb5 😦 ).

Apres un moment de flottement je tente al technique « hacker », a coup de hexdump

hexdump -C /dev/sdb5 | more

et là je tombe sur une phrase magique

 00022000  06 fd 37 87 20 4c 56 4d  32 20 78 5b 35 41 25 72  |..7. LVM2 x[5A%r|

Synology (du moins chez moi) utilise LVM  (qui n’est pas installé sur ma Linux box)

apt-get install lvm2

Et là

#mdadm -A /dev/md2 /dev/sdb5
mdadm: /dev/md2 assembled from 1 drive - need all 2 to start it (use --run to insist).
Pas de bol il ralle car le RAID n'est pas complet
# mdadm -S /dev/md2
mdadm: stopped /dev/md2
# mdadm -A /dev/md2 --run /dev/sdb5
mdadm: /dev/md2 has been started with 1 drive (out of 2).

Et là maintenant

#pvscan 
  PV /dev/md2   VG vg1   lvm2 [1,36 TiB / 0    free]
  Total: 1 [1,36 TiB] / in use: 1 [1,36 TiB] / in no VG: 0 [0   ]
# vgscan 
  Reading all physical volumes.  This may take a while...
  Found volume group "vg1" using metadata type lvm2
# lvscan 
  ACTIVE            '/dev/vg1/lv' [1,36 TiB] inherit
# mount /dev/vg1/lv /mnt/syno/
# df
[...]
/dev/mapper/vg1-lv   1440839828 1214608956 226128472  85% /mnt/syno

Je respire …

Donc pour résumer, pour accéder à un disque RAID1 (les autres aussi probablement) issu d’un synology, il faut installer les packages mdadm et lvm2. Ensuite il faut réactiver le raid mdadm -A /dev/md2 –run /dev/sdb5.

Je ne sais pas par contre si l’étape pv/vg/lvscan est nécessaire et si le système voit les nouveaux device LVM sans ça.

EDIT: Au boot Ubuntu demande s’il doit démarrer le RAID en mode dégradé, si mdadm et lvm sont installé il arrive à rendre disponible /dev/vg1/lv sans manip supplémentaire

Mise en graphique des données

Dans les billets précédents j’ai donc détaillé comment récupérer des température depuis l’Arduino, comment les véhiculer sur le réseau à travers un modèle client/serveur et comment les insérer dans une base de données.

Tout cela est bien beau mais des données non exploitées ne servent à rien.

Deux écrans de consultation me viennent naturellement à l’esprit :

Une consultation de la dernière valeur (un peu comme le ferait un thermomètre accroché au mur), cet écran est trivial donc pas la peine d’en parler.

Graphique de la station météo

Graphique de la station météo

Une visualisation d’un historique, cet historique sera constitué des moyennes sur chaque heure et restitués sur un graphique a deux axes (d’un coté les températures et de l’autre les mesures de lumière).  J’ai utilisé une librairie de chez Google dont la documentation se trouve ici .Je vous encourage a vous balader dans la doc, il y a des fonctionnalités assez intéressante d’autant plus que son utilisation est assez aisée. La génération du graphique que je souhaite rendre est fait coté client par du JavaScript.

En moins d’une heure j’ai donc codé un graphique, ce n’est peut être pas très « sexy » mais les données sont restituées et c’était là mon but.

Comme d’habitude le code est dans mon répository Google Code

Avec des Plugins c’est plus festif

Il y a deux semaines je vous avais parlé de mon Bus Arduino (qu’entre temps j’ai nommé ARV, comme Arduino Rendez-Vous). J’ai décidé de le faire évoluer pour que le collector puisse faire d’autres choses.

J’ai imaginé plusieurs architectures pour pouvoir évoluer facilement suivant les besoins et j’en suis arrivé à développer des plugins pour le collector.

Un plugin c’est quoi ?

Un plugin c’est une librairie partagée (dll ou .so suivant votre plateforme) ayant des noms de fonction normalisée.

Tous les plugins pour une même application auront donc des fonctions et des variables en communs : celles qui seront appelées par le programme « maitre ».

Ce programme va donc ouvrir chaque plugin à charger et récupérer un pointeur sur chaque fonction/variable qu’il compte utiliser.

Comment ça s’implémente ?

On commence par « ouvrir » le plugin

void * plugin = dlopen (fileName, RTLD_NOW);
 if (!plugin)
 {
  printf ("Cannot load %s: %s\n", fileName, dlerror ());
  return 1;
 }

et après on peu « lier » une variable  (par exemple la variable pluginName contenu dans le plugin)

char * pPluginName = dlsym (plugin, "pluginName");
result = dlerror ();
if (result)
[...] traitement des cas d'erreur

ou une fonction

typedef int (*init_f) ( char * _subject, sirElement * config );
init_f init = dlsym (plugin, "init");
 result = dlerror ();

Notez bien le typedef du prototype de fonction.

Pour chaque plugin chargé je stocke un pointeur vers une fonction d’initialisation du plugin (init), qui va me permettre d’ouvrir des fichiers ou de me connecter à une base de donnée, et une fonction de traitement du message reçu (parse).

Le chargement ? quel chargement ?

Oui, car il faut charger tous les plugins qu’on compte utiliser, j’ai opté pour un fichier de paramétrage (collector.ini dont un exemple est reproduit ici) qui va définir les plugins à charger et quand les utiliser.

[base]
pluginDir=plugins
[plugins]
arduinoTP.StationMeteo=stationMeteo.so
arduino.StationMeteo=stationMeteo.so
arduinoTP.StationMeteo=toFile.so
[stationMeteo]
DBHost=127.0.0.1
DBName=BaseArduino
DBUser=UserArduino
DBPassword=MotDePasseArduino
[toFile]
#Par défaut le plugin prends "toFile.csv", on peut aussi le fixer par configuration
fileOut=
[toFile:arduinoTP.StationMeteo]
#On peu aussi définir des paramétrage pour un couple plugin/sujet
fileOut=fichierOut.csv

Détaillons ce fichier de configuration

La section [base] contient pour le moment le chemin vers le répertoire des plugins. Tous les plugins devront être dedans au risque de ne pas être chargés.

La section [plugins] détaille les plugins à charger. Attardons nous un peu plus sur cette syntaxe.

arduinoTP.StationMeteo=stationMeteo.so

Il y a donc une clef  (arduinoTP.StationMeteo) qui à une valeur (stationMeteo.so),par cette directive on définie quel plugin utiliser (stationMeteo.so) lors de la réception d’un message de type « arduinoTP.StationMeteo ». C’est la fonction parse() du plugin qui sera utilisée pour traiter le message.

Le plugin stationMeteo.so a pour mission de stocker dans une base MySQL les message reçus (deux photoresistances et 5 capteurs de température), dans le but d’être accessible sur mon site web personnel.

Il est possible de mettre plusieurs ligne ayant une même clef afin de multiplier les actions à réaliser sur réception d’un message (par exemple stocker un message en base ET faire agir un autre arduino en envoyant un autre message sur le bus)

Le plugin de type stationMeteo a droit à une section de paramétrage, j’y ai placé les paramètres d’accès à la base de donnée.

[stationMeteo]
DBHost=127.0.0.1
DBName=BaseArduino
DBUser=UserArduino
DBPassword=MotDePasseArduino
J’ai écrit un autre plugin : toFile.so qui se contente d’écrire ce qu’il reçoit dans un fichier. C’est aussi une bonne méthode pour illustrer une autre façon de paramètrer des plugins.
[toFile]
#Par défaut le plugin prends "toFile.csv", on peut aussi le fixer par configuration
fileOut=
[toFile:arduinoTP.StationMeteo]
#On peu aussi définir des paramétrage pour un couple plugin/sujet
fileOut=fichierOut.csv

toFile a deux sections de configuration : [toFile] et [toFile:arduinoTP.StationMeteo]. Dans le second cas vos pouvez reconnaître un nom de message qui a déjà été paramétré dans la section [plugins]. Cela implique qu’un message de type arduinoTP.StationMeteo utilisera un plugin initialisé grâce à la section [toFile:arduinoTP.StationMeteo] et [toFile] dans les autres cas.

Dans le cas présent cela influe sur le nom de fichier, mais cette méthode est applicable par tout les plugin installé pour avoir des particularité suivant le message reçu (base de donnée différente, fichier différent, …).

Concernant la lecture du fichier d’ini

Par défaut le C n’a pas de librairie d’accès standard pour ce genre de fichier alors j’en ai ré-implémenté une aux fonctions minimales (sir.c dans le repository). J’avais d’abord cherché à utiliser ce qui existe déjà mais j’avais la contrainte de la taille (n’oublions pas que je compile pour une machine ayant 1.5Mo de filesystem ^^) , dans mes pérégrinations je suis tombé sur iniParser qui fait le boulot de recherche dans un .ini mais qui n’accepte pas d’avoir plusieurs clefs identiques, et comme j’utilise cette fonction pour multiplier les actions sur réception d’un message … j’ai été contraint d’en réécrire un plus simple.
L’accès au code se trouve comme d’habitude : ici