Un bot IRC à la CIA en python

date
9 / 5 / 2010
comments
0

Quand vous utilisez git à plusieurs et qu'en plus vous utilisez IRC pour en discuter, il peut être agréable d'avoir un bot qui informe le canal des commit sur le serveur. En particulier pour éviter des merge branch à tout va si on oublie de pull avant de commit. Il y a un service sur le net qui propose de tels bots IRC, c'est CIA.vc. Sauf que le service est à des fins de statistiques sur les logiciels libres et j'aime pas trop que ces données sortent du serveur même si elles sont publiques, puis leur bot on sait pas trop ce qu'il fait.

Donc je me suis mis à coder ce petit bot en python qui se connecte sur le canal IRC et attend des ordre via xmlrpc, cet ordre est lancé à chaque commit via un hooks sur le serveur git.

Pour ça, tout est dans la librairie python standard, sauf irclib et gitpython.

Le serveur (j'ai enlevé toutes les fonctionnalités qui ne rentrent pas dans le cadre de ce billet) :

#!/usr/bin/env python

import threading
import irclib

chan = '#staff'
nick = 'napalm0'
server = ('irc.philpep.org', 6667)
xmlrpcserver = ('localhost', 8888)

class PbotIRCClient(irclib.SimpleIRCClient):
    def on_welcome(self, s, e):
        s.join(chan)

# Classe de contrôle du bot par xmlrpc
class PbotControl:
        def commit(self, name, files, summary, base, id):
                # On formate la chaine de commit (Projet: Commiter (fichiers_touchés) sommaire_du_commit
                str = '%s: %s (\x0323%s\x03) %s' % (base, name, files, summary)
                pbot.connection.privmsg(chan, str)
                # Et un lien vers le commit diff
                pbot.connection.privmsg(chan, 'http://git.philpep.org/'+base+'.git/commit/?id='+id)

# Le serveur xmlrpc
class PbotXMLRPCServer(threading.Thread):
        def run(self):
                obj = PbotControl()
                srv = SimpleXMLRPCServer(xmlrpcserver, allow_none=True)
                srv.register_instance(obj)
                srv.serve_forever()

if __name__ == '__main__':
    pbot = PbotIRCClient()
    pbot.connect(server[0], server[1], nick)
    t = PbotXMLRPCServer()
    t.start()
    pbot.start()

Et le hooks à mettre dans le bare repo hooks/post-commit :

#!/usr/bin/env python

from sys import argv
from git import Repo
from xmlrpclib import ServerProxy

# Path vers le bare repo (peut être avec '.' ça marche aussi ?)
r = Repo('/usr/home/git/repositories/pblog2.git')
c = r.commit(argv[1])
s = ServerProxy('http://localhost:8888', allow_none=True)
# Liste des fichiers touchés par le commit
try:
        file = ' '.join([e.a_blob.path for e in c.diff(r, c.parents[0])])
except:
        file = ''
# Appel de la méthode xmlrpc
s.commit(c.author.name, file, c.summary, 'pblog2', argv[1])

Voilà, en quelques lignes de python vous avez un bot sympa et vous savez comment il fonctionne laissez parler votre imagination pour l'améliorer.

Mon nouveau jouet

date
5 / 5 / 2010
comments
0

Pris un coup de folie et je me suis procuré le dernier joujou à la mode, un petit nokia n97. Les forfaits c'est vraiment du viol^Wvol, donc clairement je ne vous recommande pas ça, moi j'ai eu l'opportunité de ne pas le payer donc bon. Ce sera nettement plus convi quand il y aura un opérateur mobile valable avec du vrai internet.

En attendant tout ça j'ai quand même pu faire des choses sympa, faut dire que Nokia, contrairement à la pomme, laisse pas mal de liberté essentiellement parce qu'on peut installer tout ce qu'on veut sur la machine et ils fournissent tous les outils pour. J'ai le bestiau depuis seulement quelques jours donc j'ai pas encore trop réfléchi sur le coté dev, mais je pense qu'on peut sortir de la prison Internet by orange avec quelques tools réseau bien pensés.

Unes des premières appli que j'ai testé c'est putty mobile. En wifi sur le réseau local je passe par le port 22 tout marche normal. Seulement sur internet c'est pas sur le port 22 qu'il est mon serveur mais plutôt disons 1234, et avec Internet by orange forcément le port 1234 sortant ça pourrait bien être du contenu pédonazi et alors ça passe pas.

Du coup je browse sur un de mes sites, je chope l'IP 80.10.46.66 de la passerelle nazie dans mes logs, comme c'est bien pensé l'IP de la passerelle change souvent et faut plutôt utiliser 80.10.46.0/24 (voir plus si ça se trouve).

Un petit coup de pf.conf sur la box :

nazis = "80.10.46.0/24"
rdr pass proto tcp to port 1234 -> $serv port ssh # Pour les gens normaux
rdr pass proto tcp from $nazis to port ssh -> $serv

Et taaadaam :

putty symbian

Sûrement à bientôt pour de nouvelles aventures Internet By Orange.

blog.philpep.org rune pblog2

date
2 / 5 / 2010
comments
0

Ça bouge beaucoup sur le git de pblog2 en ce moment. Même si on est encore loin d'avoir une version stable, il est pleinement utilisable et apporte son lot de nouveautés :

  • Interface d'administration très complète
  • Gestion de pages statiques
  • Possibilité de changer et de créer des nouveaux templates
  • Gestionnaire de médias (pour uploader quand on peut pas scp)
  • Post différés
  • Interface d'édition markdown convi en javascript
  • Installation en HTTP
  • Plus de fichier de configuration (tout se fait par HTTP)

Bender s'est mis à fond sur le javascript, pblog2 utilise maintenant jQuery qui permet de faire des petites choses sympa (bien entendu pblog2 restera au maximum utilisable sans javascript).

On peut maintenant créer son propre design (et donc choisir l'emplacement des divers éléments, nuage de tags, archives etc). Pour l'instant pblog2 est distribué avec deux designs (White-line et ouverta le petit nouveau que vous voyez sur ce blog).

Grand changement notable : pblog2, à contrario de pblog, utilise SQLAlchemy, ce qui permet d'avoir un code plus court, plus simple et en même temps plus optimisé. En plus pblog2 peut tourner maintenant sous toutes les bases de données gérés par sqlalchemy et il y en a un paquet.

Pour la migration de pblog1 vers pblog2 j'ai codé une petite fonction, vous avez juste à donner le chemin d'installation de pblog1 et pblog2 va importer tous vos posts/commentaires/tags.

Il reste beaucoup de travail pour corriger les quelques bugs et rendre l'interface plus cohérente, en attendant n'hésitez pas à filer un coup de main, donner des idées de ce que vous aimeriez bien voir sur pblog2.

git clone git://git.philpep.org/pblog2.git

Le parseur de wmfs

date
20 / 4 / 2010
comments
0

EDIT: ce billet se voulait une vulgarisation, mais il faut quand même savoir manipuler les notions d'automates, de grammaire etc. Si ça donne envie d'en savoir plus, tant mieux.

En même temps que j'ai laché dwm pour revenir au bon vieux (pas si vieux que ça) wmfs, j'ai totalement recodé le parseur du fichier de configuration.

Un fichier de configuration se doit d'être lisible, et sa projection en mémoire doit être facile à manipuler, malheureusement utiliser le même langage (comme le fait dwm avec son fichier de configuration directement en C) rend la configuration moins convi pour peu que l'on ne soit pas expert. Le parseur sert à ça, traduire un langage simple en langage compréhensible par la machine.

L'idée c'est d'expliquer un peu comment le parseur de wmfs fonctionne parce que ça touche une partie de l'informatique théorique que j'aime bien : l'étude des langages formels.

Yacc et lex sont souvent utilisés pour ce genre de choses. Mon code est juste une implémentation C d'un automate fini déterministe, il ne doit pas être loin du fonctionnement de yacc pour une grammaire et une syntaxe particulière (évidement yacc&lex sont beaucoup plus évolués que mon petit bout de code).

Donc le travail de lex c'est de lire le fichier caractère par caractère et de renvoyer des token, c'est à dire un identifiant syntaxique.

Le langage de configuration est comme ça :

[section]
    option = valeur
    truc = { "liste", 1, False }
    [sous-section]
        [sous-sous-section]
            machin = True
        [/sous-sous-section]
    [/sous-section]
[/section]

Je fais une première transformation purement syntaxique :

enum conf_type { SEC_START, SEC_END, WORD, EQUAL, LIST_START, LIST_END, NONE };
/* qui correspond à  [        [/    un_mot  =        {         }       autre */

Et je range ça dans deux listes chainées, une qui contient des conf_type et une autre qui contient les mots (des char *) :

Liste des TOKEN             Liste des mots (WORD)
SEC_START
WORD                        "section"
WORD                        "option"
EQUAL
WORD                        "valeur"
WORD                        "truc"
EQUAL
LIST_START
WORD                        "liste"
WORD                        "1"
WORD                        "False"
SEC_START
WORD                        "sous-section"
SEC_START
WORD                        "sous-sous-section"
WORD                        "machin"
EQUAL
WORD                        "True"
SEC_END
WORD                        "sous-sous-section"
SEC_END
WORD                        "sous-section"
SEC_END
    WORD                        "section"

Une fois ces deux piles construites, il suffit de dépiler en suivant la grammaire (c'est ce que fait yacc). C'est là qu'on parle d'états finis. Désolé ça aurait été plus compréhensible avec un schéma, mais je sais pas en faire donc j'ai écrit ça avec la syntaxe qu'on utilise pour décrire des grammaires.

start: /* rien */
       | SEC_START WORD sections SEC_END WORD start /* une suite de sections */
       ;

sections: /* rien */
           | SEC_START WORD sections SEC_END WORD /* des sous sections */
           | WORD EQUAL option sections /* des options */
           ;
option: WORD /* un mot tout simple (une valeur quoi) */
          | LIST_START list LIST_END /* une liste de valeurs */
          ;
 list: /* rien */
     | WORD list /* une liste de mots */
     ;

Le parseur va ainsi remplir une structure générique capable de contenir toutes les configurations qui respectent la grammaire et la syntaxe.

Pour wmfs en gros c'est ça :

struct conf_opt { /* options */
    char *name;
    char *val[10]; /* au plus 10 éléments dans une liste */
    SLIST_ENTRY(conf_opt) entry;
}
struct conf_sec { /* section */
    char *name;
    SLIST_HEAD(, conf_opt) optlist; /* liste chaînée des options de la section */
    TAILQ_HEAD(, conf_sec) sub; /* liste chaînée des sous sections */
    TAILQ_ENTRY(conf_sec) entry;
}

J'utilise abusivement des macros de <sys/queue.h>.

Ensuite il suffit de coder une API qui va récupérer des options/sections précises sur la structure, mais c'est pas le plus dur à faire.

Voilà un peu l'idée, l'intérêt de cette méthode c'est qu'elle est certaine (pas ou peu de bugs incompréhensibles), rapide : le fichier de configuration n'est traversé qu'une seule fois, pas besoin de strlen(), strchr(), strcmp(), strstr() qui prennent des plombes.

EDIT : j'ai failli oublier de donner le lien vers le code

les coredump sous Linux

date
15 / 4 / 2010
comments
0

Sous FreeBSD les coredump sont automatiquement générés, voyez plutôt l'entrée default de /etc/login.conf(5) avec le coredumpsize=unlimited.

Pour faire la même chose sous Linux (en l'occurrence sur Archlinux ce n'est pas le cas par defaut) :

Dans /etc/sysctl.conf :

kernel.core_pattern = %e.core

Et pour rendre la taille des .core illimitée j'ai édité mon ~/.zprofile :

ulimit -c unlimited

Et pour rendre les changement effectifs :

sysctl kernel/core_pattern=%e.core
# Si vous n'avez pas sysctl :
echo "%e.core" | sudo tee /proc/sys/kernel/core_pattern
# et on se reconnecte

Et pour tester :

cat > foo.c << EOF
#include <stdlib.h>
int main(void) { char *p = malloc(sizeof(*p)); free(p+1); return 0; }
EOF
cc foo.c && ./a.out
[.....]
zsh: abort (core dumped)  ./a.out
# \o/
gdb ./a.out a.out.core
....