lundi 19 novembre 2007

Bouton power sur un PowerMac G4 sous Gentoo

J'ai hérité d'un sympathique petit PowerMac G4, que j'utilise à la maison comme serveur de stockage. Il tourne sous Gentoo Linux PPC.

Dans la configuration par défaut, il y a un petit détail ennuyeux: le bouton Power ne fait rien par défaut. Utilisant mon serveur sans écran ni clavier, je devais me connecter avec SSH depuis mon portable pour l'éteindre, ce qui est peu pratique. Le petit truc suivant m'a permis de configurer le bouton Power pour éteindre la machine proprement:

  1. Un coup de showkeys en root via ssh, et appuyer sur le bouton. Chez moi, showkeys indique que le bouton est reconnu comme touche de clavier standard (keycode 116).
  2. Dumpkeys indique que la touche est reconnue comme touche de clavier "Do":

  3. dumpkeys |grep 116
    keycode 116 = Do

  4. Ajoutons au script de boot (pour gentoo, /etc/conf.d/local.start fait l'affaire) un mapping qui associe le symbole KeyboardSignal à cette touche:

  5. loadkeys - <<< 'keycode 116 = KeyboardSignal'
  6. Configurons init pour intercepter ce KeyboardSignal et éteindre la machine, en ajoutant cette ligne dans /etc/inittab:

  7. ha:12345:kbrequest:/sbin/shutdown -h now

Voila :) Si j'ai le temps, je ferai une version sûre contre les extinctions accidentelles.

jeudi 8 novembre 2007

Screenshot PLT 0xA

Un petit screenshot du nouveau plan de réseau pour PolyLAN 0xA

En direct du local PolyLAN...

La configuration progresse à petits pas... Comme toujours, je perds mon temps en gadgets inutiles (ou pas). Dans la liste des fonctionnalités manquant à PLT, on peut maintenant barrer:
  • Des quittances pour les alertes sur l'état des trunks. Une touche dans le trunk monitor permet d'arrêter le clignotement, l'alarme est supprimée jusqu'a ce que le trunk récupère un état normal.
  • Un script wiring-map.pl permet d'obtenir une vue graphique de la configuration d'un switch donné dans la DB. Le script affiche la disposition des différents trunks et leurs destinations supposées, et les ports utilisateur déclarés.
  • Le code de gestion des traps SNMP a été largement réécrit pour être polymorphique, il est maintenant possible de traiter les traps en fonction du modèle de switch qui les envoie; il n'est plus nécessaie de maintenir de multiples entrées dans snmptrapd.conf, une seule entrée par défaut suffit.
Autrement, les backbones sont complètement configurés, et je vais commencer la configuration des 6 derniers switches 3424 (mise a jour firmware + configuration PolyLAN)

lundi 5 novembre 2007

OIDs de versions sur Dell PowerConnect

SNMPv2-SMI::mib-2.47.1.1.1.1.9.67108992
SNMPv2-SMI::mib-2.47.1.1.1.1.10.67108992
SNMPv2-SMI::mib-2.47.1.1.1.1.8.67108992

Ces OIDs permettent de récupérer les versions du bootcode, du firmware et du matériel des switches Dell PowerConnect. Confirmé sur 3424 et sur 5324.

PLT inclut maintenant un script ./check-versions.pl qui affiche les versions des firmwares des équipements connectés.

vendredi 2 novembre 2007

CaveShooter 0.3

Ca-y-est, CaveShooter a atteint une version utilisable ! on peut se tirer dessus tout en discutant. Après quelques ajustements au gameplay, c'est devenu carrément amusant.

Reste a y intégrer quelques cheat codes... je verrai ça quand j'ai le temps :)

cudoku 0.2 !

Voila une nouvelle version de CuDoKu, mon solveur de sudoku en curses !

  • Il est maintenant possible d'importer et de sauvegarder les grilles
  • Pour les cas ou l'algorithme d'induction-déduction ne suffit pas, le solveur effectue une descente récursive
Pour rappel, cudoku est écrit en Perl pur, et utilise une interface Curses (nécessite le module Curses de perl).

Vous pouvez obtenir la dernière version via mon serveur Mercurial tout neuf:
hg clone https://xolus.net/hg/cudoku


Ou simplement le tarball:
https://xolus.net/~max/cudoku-0.2.tar.gz

lancez simplement avec ./cudoku . La grille démarre vide si le fichier n'existe pas.

Une grille enregistrée est composée de neuf lignes de neuf caractères, pouvant être les chiffres de 1 à 9 pour les valeurs connues, ou 0 si la case est inconnue. Le programme est souple et acceptera des caractères arbitraires entre les chiffres.

N'hésitez pas à y apporter des améliorations et à me soumettre par mail des patches Mercurial:

(hack hack hack...)
hg commit -m 'uber-cool changes'
hg bundle mes-ameliorations.hg https://xolus.net/hg/cudoku
(envoyer mes-ameliorations.hg par mail)

jeudi 1 novembre 2007

Du bon usage de /finally/

Dans le cadre du cours de réseaux informatiques, nous constatons malheureusement quelques légères difficultés avec de la matière de première année... un point particulièrement problématique est celui du traitement des exceptions en Java.

En particulier, la formation en première année (selon mes souvenirs) ne se concentre que sur l'aspect interception des exceptions, comme une nuisance mineure mais inévitable; la création d'exceptions propres au programme, et le report du traitement des erreurs, sont malheureusement mis de coté. Je ne crois pas avoir jamais fait d'exercice consistant a remonter une exception dans un cas concret.

L'intérêt des exceptions est évidemment de pouvoir déférer le traitement d'erreurs a une couche plus élevée du code, la ou il est possible de prendre une décision pertinente quant au traitement de l'erreur, et ce sans saturer l'interface de la méthode.

Exemple: voici une fonction sensée construire un objet 'blob' à partir d'un fichier:


Blob lire(String fichier) {
Blob b = ...
FileInputStream f = ...
...
f.close();
return b;
}

Mais si le fichier ne peut être lu ? d'ailleurs, le code précédent ne compile même pas; il y a plein d'exceptions IOException non récupérées. Que faire ? j'ai vu beaucoup de gens tomber dans le travers suivant:


Blob lire(String fichier) {
try {
Blob b = ...
...
return b;
} catch (Exception e) {
return null;
}
}


FAUX !!!
Non seulement le code appelant risque de se manger une NullPointerException à tout moment, mais en plus on perd complètement la traçe de l'erreur initiale. Et en plus, si un problème survient pendant la lecture (formatage, etc...) le fichier ne sera jamais fermé !

On réessaie:


Blob lire(String fichier) {
try {
FileInputStream f = ...
//fichier bien ouvert, il faut le fermer
Blob b;
try {
...
} catch (IOException e) {} //Erreur pendant lecture
f.close();
return b;
} catch (Exception e) {
return null;
}
}

ARCHI-FAUX !!!

La encore, on retourne un b qui risque d'être incorrect si le parsing n'a pas marché. Sans compter la laideur... notez qu'on ne peut retourner le b avant le f.close(), puisque le return termine la fonction, forcément...

On nous a enseigné que la syntaxe appropriée est:

try { ... } { { catch(Type e) { ... } } } finally { ... }

en première année, on se contente de dire que "le contenu du bloc finally est toujours exécuté après le contenu du bloc try, même si celui-ci a levé une exception.

Naif étudiant en première que j'étais, je me suis posé la question: Mais quelle est donc la différence entre:


try {
... //throws MyException
} catch (MyException e) {
...
} finally {
... // blah blah
}


et


try {
... //throws MyException
} catch (MyException e) {
...
}
// blah blah


? Dans les deux cas, si le contenu du bloc try lève une exception MyException, celle-ci est attrapée, et le code "blah blah" est exécuté. Alors pourquoi cette clause finally ?

Ce qui n'a pas assez été répété, c'est que le code dans le "try" peut très bien lever une autreexception que celle de la clause catch (qui est d'ailleurs facultative). D'une part, le code peut lever une exception de type RuntimeException (dont l'interception n'est pas obligatoire, car de telles exceptions sont considérées imprévisibles), d'autre part, la méthode contenant le bloc try/finally peut très bien déclarer elle-même lever des exceptions. Enfin, sachez également qu'il est possible de faire un "return" a l'intérieur d'un bloc try/finally, et que la clause finally sera exécutée tout de même !

Ce qui nous amène naturellement a la version légèrement plus correcte de l'exemple précédent:


Blob lire(String fichier) throws IOException {
FileInputStream f = ...
//fichier ouvert
try {
Blob b = ...
...
return b;
} finally {
f.close();
}
}


Notez que nous n'avons utilisé qu'un seul try/catch, que nous n'avons pas besoin de prédéclarer b dans un autre bloc (ce qui est laid), que f.close() sera toujours appelé si le fichier a été correctement ouvert, que le b retourné sera toujours correct, que le programme appelant aura a sa disposition toute l'information nécessaire pour réagir correctement à l'erreur, et que le compilateur informera le programmeur si le code de traitement d'erreur est oublié.