Nouvelle version de la documentation pour le demon oupsd.

[image de l'auteur]
par Rémi Suinot

L´auteur:

Je suis infirmier, je programme pour le plaisir, sous linux et un peu sous PIC. Je suis l'instigateur du projet Domotique (gestion de chauffage électrique sous linux). Actuellement, je tente de créer un jeu de domino en réseau.
Sous linux depuis plusieurs années, j'apprécie sa grande souplesse d'utilisation ainsi que son grand nombre de fichiers de log.



Arretez votre machine sans clavier ni réseau

[THE Hardware!] [THE Hardware!]

Résumé:

Le but du «jeu», est d'éteindre une machine sous linux, lorsque l'on n'a plus de réponse du clavier, ni de connection par réseau.

Introduction

Mes premières machines avaient des carte graphique S3, avec lesquels j'ai toujours eu des ennuis. Le plus pénible était de se retrouver sans clavier ni souris, mais avec une machine dont le noyau continuait à fonctionner (ça, je le voyais après redémarrage, dans les fichiers de log). Et puis, un jour, je suis tombé sur un courriel, du goupe usenet fr.comp.sys.linux, demandant si l'on pouvait simuler un onduleur! L'auteur donnait un lien, et quelques détails, mais n'ayant jamais retrouvé trace de ce texte, je n'ai pu vérifier l'utilité. Au bout d'un certain temps, voire le système effectuer une analyse du disque dur à chaque redémarrage m'a convaincu de (re)créer un petit systeme qui simule un onduleur, et demande poliment au noyau de stoper la machine.

A noté q'après avoir quasiment terminé mon projet, j'ai trouvé un système équivalent (cf liens).

 

Réalisation du hardware

Mon appareil est branché sur le port série (ou RS232), qui est le port par défaut pour les périphériques de type modem ou onduleur (justement!). La documentations de départ repose sur le Serial Howto, et surtout sur l'UPS Howto.

N'importe quelle machine dispose aujourd'hui de un ou deux ports de ce type, à quelques exeptions près (portable récent, par exemple: aucun port RS232!). Il prend la forme d'un connecteur 9 broches (25 anciennements), dénommé ttyS0 ou ttyS1. Le schéma ci-dessous montre le brochage et le sens des signaux.
Broche Nom Signification Sens  
Prise DB9 femelle vue de devant.
1 DCD Data Carrier Detect Entrée
2 RxD Receive Data Entrée
3 TxD Transmit Data Sortie
4 DTR Data Terminal Ready Sortie
5 GND masse -
6 DSR Data Set Ready Entrée
7 RTS Request To Send Sortie
8 CTS Clear To Send Entrée
9 RI Ring Indicator Entrée

Le port série est très bien protégé contre les courts-circuits (avec une intensité de 20mA) et les broches sont limitées en courant. Ainsi, il est tout à fait possible d'alimenter un petit montage électronique (les souris séries sont alimentées par la ligne RTS). Notre montage ne necessitant pas d'alimentation, nous n'aurons pas de soucis de ce côté, et pour les signaux que nous allons utiliser, nous limiterons quand même le courant avec une petite résistance. Et nous pouvons quand même placer une ou deux diodes, histoire de donner un peu de couleurs (mais ce n'est pas obligé)

Notre connection sera faite avec une prise femelle (cf dessin ci dessus)

Ce dont nous avons besoin, c'est d'une résistance de 1 kilo ohm, de deux petites diodes (une rouge et une jaune, par exemple) et d'un interrupteur 3 positions, pour indiquer le type d'arret que l'on veux:
au repos fonctionnement normal
position 1 arret du serveur X (à priori, c'est lui qui bloque le clavier): diode jaune
position 2 arret complet ou redémarrage du système: diode rouge

L'astuce, c'est de prendre un signal en sortie (broche 3, 4 ou 7), puis de l'injecter vers une broche en entrée (broche 1, 2, 6, 8 ou 9). Ensuite, il faut regarder ce qui se présente sur les sorties. Ca, se sera le rôle de la partie logiciel.

La réalisation ne suppose pas de gros efforts:
Version sans diode Version avec diode
soudez un bout de la résistance sur la broche 4 du connecteur: ce sera notre signal en sortie; soudez un bout de la résistance sur la broche 4 du connecteur: ce sera notre signal en sortie;
soudez l'autre bout au point milieu de l'interrupteur; soudez l'autre bout au point milieu de l'interrupteur;
soudez un petit bout de fil electrique entre la broche 6 et l'un des picots de l'interrupteur (sauf celui du milieu!) soudez sur la broche 6 une pate de la diode rouge et l'autre pate sur un des picots de l'interrupteur (sauf celui du milieu!)
soudez un petit bout de fil electrique entre la broche 1 et le troisième picot de l'interrupteur soudez sur la la broche 1 une pate dela diode jaune et l'autre pate au troisième picot de l'interrupteur

Voir le shéma d'implantation des composants ci dessous.

Sans diode Avec diode
Et voila! Si l'on veux, on peux faire la même chose, avec un cable, afin de déporter l'interrupteur de derrière la machine, vers par exemple, la face avant de l'ordinateur. Attention à ne pas se tromper dans les fils du cable, bien sur. Personnellement, je m'en sers sur mon portable, donc pas de problème, tout est dans la prise!.

Le logiciel

C'est un peu plus compliqué. Nous allons donc commencer par préparer le terrain, faire quelques tests, puis on améliorera après.

En premier, il faut vérifier sur quelle sortie série vous avez branché votre boitier. Puis, pour ne pas rechercher à chaque fois , la solution la plus simple, c'est de créer un lien de /dev/ttyS0 par exemple, vers /dev/oups:

$ su
password:  
# ln -s /dev/ttyS0 /dev/oups
# exit
Nous ferons donc toujours référence à ce périphérique, à présent.
Selon votre ditribution, vérifiez si vous avez les autorisations de lecture/écriture sur /dev/ttySx. C'est béte, mais c'est toujours source de perturbations!
A présent, un petit test?
J'ai trouvé le programme suivant sur usenet, écrit par Alessandro Rubini pour tester le port série. (version un peu remanié pour mes besoins)
/* 
 * setserialbits.c   set/get bit values from the serial port
 *
 * This program is a minimal tool to test the serial port.
 * It can be useful to understand how your UPS is connected.
 *
 * Alessandro Rubini, rubini@ipvvis.unipv.it
 * 
 * Modification: R. Suinot, 
 * Afin de limiter le teste sur les signaux DCD et DSR
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <errno.h>

int main(int argc, char **argv)
{
int fd, timeout=0;
char *me=argv[0];
extern int errno;
uint bits, mask=~0, outb=0;

  if (argc < 2 || !strcmp(argv[1],"--help") || !strcmp(argv[1],"-h")) {
    printf("%s: Usage: \"%s <device> [options]\"\n",me,me);
    printf("    <device>        is a serial device, like /dev/cua0\n"
           "    a number        report data every that many seconds\n\n");
    exit(1);
  }

  if ((fd=open(argv[1], O_RDWR | O_NDELAY)) < 0) {
      fprintf(stderr, "%s: %s: %s\n", me, argv[1], strerror(errno));
      exit(1);
  }

  while(++argv,--argc) {
    if (isdigit(**argv))
       timeout=atoi(*argv);
  }

  mask &= ~TIOCM_DTR;
  outb |= TIOCM_DTR;

  ioctl(fd,TIOCMGET,&bits);
  bits&=mask;
  bits|=outb;
  ioctl(fd,TIOCMSET,&bits);

  printf("DSR DCD\n");
  do {
    ioctl(fd,TIOCMGET,&bits);
    printf(" %i   %i\n", (bits & TIOCM_DSR) != 0,
			  (bits & TIOCM_CD) !=0);
    sleep(timeout);
  }
  while(timeout);

  exit(0);
}
Appelez ce programme "genteste.c", puis compilez:
$ gcc -Wall genteste.c -o genteste
Puis éxécutez le:
$ ./genteste
Voici le résultat chez moi:
$ ./genteste /dev/oups 1
DSR DCD
 0   0
 0   0    <-- bouton au point milieu
 0   0
 1   0
 1   0    <-- position 1 (diode rouge allumée
 0   0
 0   1
 0   1    <-- position 2 (diode jaune allumée)
 0   1
 0   0
 0   0
Terminez par 'Control C' pour stopper le processus.
Ce test est positif, nous allons à présent faire en sorte que le système réagisse à notre montage.

D'abord, voyons comment doit ce dérouler la gestion d'un signal:

  1. un demon fonctionne en permanence, et scrute le port série;
  2. des qu'un signal arrive, celui ci regarde lequel, et envoi une commande au processus init;
  3. init regarde dans son fichier de configuration ce qu'il doit faire de ce signal:
  4. ici, il doit appeler un script, avec un paramêtre spécial, en fonction du signal envoyé à init;
  5. enfin, execution du script selon la commande désiré.

Voyons les divers éléments:

  1. Le demon oupsd

    Celui ci ne fait guère plus que le petit teste ci-dessus. A l'initialisation, on le fait passer en tache de fond, puis il scrute le port série passé en paramètre selon une durée passé aussi en paramètre. Dès qu'il détecte un changement, il se met en attente et regarde encore trois fois ci le changement persiste, puis s'il n'y a pas eu de contre-ordre, envoi un signal au processus init. Accesoirement, il emet un 'bip' puis deux, puis trois. Le code est commenté et ne présente pas de difficultée. Il doit être ici.

  2. Le fichier de configuration d'init

    Le fichier de configuration du demon init s'appel inittab (cf: /etc/inittab). Pour la gestion des onduleurs, nous avons déjà tout de préparé:

    ~/$ cat /etc/inittab | grep power
    # What to do when the power fails/returns.
    pf::powerwait:/etc/init.d/powerfail start
    pn::powerfailnow:/etc/init.d/powerfail now
    po::powerokwait:/etc/init.d/powerfail stop
    	
    Nous voyons que selon le signal reçut (pf, pn ou po), le script powerfail est appelé avec un paramètre spécifique. Pour le projet, je garde le nom powerfail, c'est assez explicite, mais je le réécrit, et donc les paramètres changent:

    $ cat /etc/inittab | grep power
    # What to do when the power fails/returns.
    pf::powerwait:/etc/init.d/powerfail stop-x
    pn::powerfailnow:/etc/init.d/powerfail stop-init
    po::powerokwait:/etc/init.d/powerfail restore          # celui-ci ne sert pas.
    	
  3. Le script powerfail

    Sont contenu est assez limité:

    #!/bin/sh
    #
    # /etc/init.d/powerfail
    # ce script est execute par init lorsqu'il detecte une action sur le port série /dev/ups
    # a utiliser en conjonction avec le projet oups! 
    # et le demon oupsd
    #
    # Adapté du Howto-UPS et de la Faq-UPS
    #
    # Version 0.5 (30/01/2002)
    #
    # Auteur: R. Suinot, 
    #
    
    PATH=/sbin:/etc:/bin:/usr/bin:/usr/sbin
    
    # on regarde quel est le parametre donne au script
    
    case "$1" in
    	stop-init)
    		echo "Attention - le systeme est en phase d'arret d'urgence!"
    		echo "Arret dans 1 minute." | wall
     		/sbin/shutdown -t 60 -h now
    		;;
    	stop-x)
    		# Vous pouvez placer ici n'importe quel commande, un kill du serveur X 
    		# ou un passage en init 3 (si vous etes en init 5) par exemple.
    		echo "Attention - arret de l'interface graphique." | wall
    
    		# Vous avez le choix:
    		#   1) arret du gestionnaire graphique, mais il risque de redemarrer
    		# /usr/bin/killall gdm
    
    		#   2) idem plus haut, mais beaucoup plus propre
    		/etc/init.d/gdm stop
    		
    		#   3) si vous voulez modifier le niveau du processus init, voila la methode
    		# /sbin/init 3
    		
    		;;
    	*)
    		echo "Erreur dans les parametres reçu! " | wall
    		exit 1
    		;;
    esac
    
    exit 0
    	
    Vous pouvez modifier la manière de couper le serveur X. Soit couper gdm (ou kdm pour KDE), soit le tuer, soit changer de niveau d'execution (peut être le plus propre, si vous avez un gros problème avec le serveur X).

  4. Le script de démarrage

    Pour finir, il nous faut un script de démarrage pour le demon. J'ai utilisé le squelette fourni avec ma debian, simple mais efficace!

    #! /bin/sh
    #
    # oupsd.sh
    #               Demarrage du daemon oupsd.
    #               Script realise selon le squelette fourni dans les 
    #               distribution Debian.
    #
    #		Written by Miquel van Smoorenburg .
    #		Modified for Debian GNU/Linux
    #		by Ian Murdock .
    #
    # Version:	@(#)skeleton  1.9  26-Feb-2001  miquels@cistron.nl
    #
    # Version:	@(#)oupsd.sh  0.4  24-Jan-2002  rsuinux@gmx.fr
    #
    
    PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
    DAEMON=/usr/local/sbin/oupsd
    NAME=oupsd
    DESC="some oupsd"
    
    test -x $DAEMON || exit 0
    
    set -e
    
    case "$1" in
      start)
    	echo -n "Starting $DESC: "
    	start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \
    		--exec $DAEMON /dev/oups 5
    	echo "$NAME."
    	;;
      stop)
    	echo -n "Stopping $DESC: "
    	start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid \
    		--exec $DAEMON
    	echo "$NAME."
    	;;
      restart|force-reload)
    	#
    	#	If the "reload" option is implemented, move the "force-reload"
    	#	option to the "reload" entry above. If not, "force-reload" is
    	#	just the same as "restart".
    	#
    	echo -n "Restarting $DESC: "
    	start-stop-daemon --stop --quiet --pidfile \
    		/var/run/$NAME.pid --exec $DAEMON
    	sleep 1
    	start-stop-daemon --start --quiet --pidfile \
    		/var/run/$NAME.pid --exec $DAEMON /dev/oups 5
    	echo "$NAME."
    	;;
      *)
    	N=/etc/init.d/$NAME
    	# echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
    	echo "Usage: $N {start|stop|force-reload}" >&2
    	exit 1
    	;;
    esac
    
    exit 0
    	

Installation

Nous avons tout ce qu'il faut pour installer le projet.: Le demon oupsd: nous le mettons dans /usr/local/sbin
le script powerfail: il reste dans /etc/init.d/ (je ne comprend pas trop pourquoi il n'est pas dans /usr/sbin ou dans /sbin, mais puisque toutes les versions d'inittab sont comme ça, ne changeons rien)
le script de demarrage oupsd.sh: il se place logiquement dans /etc/init.d, puis il faut créer les liens vers les répertoires /etc/rc*.d

 

Essais?

Branchez la prise sur le port série, executez sous root le demon (à la main, pour essayez), et voyez l'effet lors d'un basculement de l'interrupteur! Selon votre distribution, vous devrez regarder dans /var/log/syslog ou dans /var/log/messages.

 

En conclusion

Ce projet n'est en aucun cas difficile à réaliser, et peut être utile. Il apporte l'occasion de se pencher sur la programmation du port série, ainsi que sur l'écriture d'un demon simple (rien a voir avec apache ou syslog!). J'espère qu'il n'y a pas trop d'erreur dans cette documentation. Si c'est la cas, n'hésitez pas à me le signaler.
(pub perso: sur mon site, vous trouverez l'archive complète oupsd-1.0.tgz)

 

Liens

UPS-HOWTO :
www.linuxdoc.org/HOWTO/UPS-HOWTO
Serial-HOWTO :
www.linuxdoc.org/HOWTO/Serial-HOWTO
Serial-Programming-HOWTO :
www.linuxdoc.org/HOWTO/Serial-Programming-HOWTO
un projet équivalent, géré par Guido Socher :
Un bouton d'arrêt sur le port série

Copyright

© 2002/2003/2004 Remi Suinot

Ce document est distribué dans le seul espoir qu'il vous soit utile, mais sans aucune garantie. Il est distribué sous la licence "GNU General Public License", version 2 ou suivante.

Ce site est continuellement en chantier, surtout la partie 'Journal de bord' (cf 'Documentations').
Cependant, vous pouvez quand même me contacter et me donner vos avis/idées/corrections à rsuinux <at> gmx.fr

Mise en place du site:   11 Juillet 2001     Dernière mise à jour: 11/05/04

Vous êtes     à être passé me voir!

Valid HTML 4.01! Valid CSS!