<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet title="XSL formatting" type="text/xsl" href="http://www.bonz.org/tech/feed/rss2/xslt" ?><rss version="2.0"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:wfw="http://wellformedweb.org/CommentAPI/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
  <title>ZenTech</title>
  <link>http://www.bonz.org/tech/</link>
  <description>Om Mani Padme Hum</description>
  <language>fr</language>
  <pubDate>Wed, 25 Jun 2008 12:38:52 +0200</pubDate>
  <copyright></copyright>
  <docs>http://blogs.law.harvard.edu/tech/rss</docs>
  <generator>Dotclear</generator>
  
    
  <item>
    <title>La tu me vois, là tu me vois plus</title>
    <link>http://www.bonz.org/tech/post/2008/06/25/La-tu-me-vois-la-tu-me-vois-plus</link>
    <guid isPermaLink="false">urn:md5:a819fb937a60911f35bea2f4ba785181</guid>
    <pubDate>Wed, 25 Jun 2008 14:38:00 +0200</pubDate>
    <dc:creator>hr</dc:creator>
        <category>truc</category>
            
    <description>    &lt;p&gt;Depuis quelques temps j'avais un petit problème avec mon pavé numérique sur ma hardy, plus rien ne fonctionnait correctement. Le problème est simple à rêgler, dans le menu &lt;code&gt;Système &amp;gt; Préférences &amp;gt; Clavier&lt;/code&gt;, onglet &lt;code&gt;Touches de la souris&lt;/code&gt; il suffit de désactiver &lt;code&gt;Permettre le contrôle du pointeur par le clavier&lt;/code&gt;. Cette option est également contrôlée par la combinaison de touches &lt;code&gt;Shift + Verr Num&lt;/code&gt;.&lt;/p&gt;</description>
    
    
    
          <comments>http://www.bonz.org/tech/post/2008/06/25/La-tu-me-vois-la-tu-me-vois-plus#comment-form</comments>
      <wfw:comment>http://www.bonz.org/tech/post/2008/06/25/La-tu-me-vois-la-tu-me-vois-plus#comment-form</wfw:comment>
      <wfw:commentRss>http://www.bonz.org/tech/feed/rss2/comments/201</wfw:commentRss>
      </item>
    
  <item>
    <title>Un robot en date et heure</title>
    <link>http://www.bonz.org/tech/post/2008/06/14/Un-robot-en-date-et-heure</link>
    <guid isPermaLink="false">urn:md5:a3cd68f99363089a4d407d5c0f47dc5a</guid>
    <pubDate>Sat, 14 Jun 2008 20:30:00 +0200</pubDate>
    <dc:creator>hr</dc:creator>
        <category>truc</category>
            
    <description>&lt;p&gt;C'est un classique et je veux vous en faire profiter, il n'y a pas de raison. Une petite tâche dans &lt;code&gt;crontab&lt;/code&gt; qui a besoin de la date courante et &lt;strong&gt;*pouf*&lt;/strong&gt; des messages d'erreur incompréhensibles&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
Syntax error: end of file unexpected (expecting &amp;quot;)&amp;quot;)
&lt;/pre&gt;

&lt;p&gt;C'est parti pour une petite investigation.&lt;/p&gt;    &lt;p&gt;Ma ligne &lt;code&gt;crontab&lt;/code&gt; ressemble à quelque chose comme ce qui suit&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
59 23 * * *     /home/hr/codaz/script.py $(date +%Y%m%d)
&lt;/pre&gt;

&lt;p&gt;Rien de bien sorcier pourtant, je fournis simplement la date courante à un script qui est lancé à 23h59 tous les jours. Et pourtant la source du problème est abordée dans la page de man de &lt;code&gt;crontab(5)&lt;/code&gt;&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
     The ``sixth'' field (the rest of the line) specifies the command to be
     run.  The entire command portion of the line, up to a newline or % char-
     acter, will be executed by /bin/sh or by the shell specified in the SHELL
     variable of the cronfile.  Percent-signs (%) in the command, unless
     escaped with backslash (\), will be changed into newline characters, and
     all data after the first % will be sent to the command as standard input.
&lt;/pre&gt;

&lt;p&gt;Il faut donc toujours penser à échapper les caractères '%' dans une ligne de &lt;code&gt;crontab&lt;/code&gt; pour éviter ce type de soucis, ma ligne devient donc&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
59 23 * * *     /home/hr/codaz/script.py $(date +\%Y\%m\%d)
&lt;/pre&gt;

&lt;p&gt;Je ne devrais plus oublier cette fois-ci!&lt;/p&gt;</description>
    
    
    
          <comments>http://www.bonz.org/tech/post/2008/06/14/Un-robot-en-date-et-heure#comment-form</comments>
      <wfw:comment>http://www.bonz.org/tech/post/2008/06/14/Un-robot-en-date-et-heure#comment-form</wfw:comment>
      <wfw:commentRss>http://www.bonz.org/tech/feed/rss2/comments/194</wfw:commentRss>
      </item>
    
  <item>
    <title>Je te casse et tu ne peux rien y faire</title>
    <link>http://www.bonz.org/tech/post/2008/04/29/Je-te-casse-et-tu-ne-peux-rien-y-faire</link>
    <guid isPermaLink="false">urn:md5:af4c0f57492848a8c27d1842e426cdd8</guid>
    <pubDate>Tue, 29 Apr 2008 12:29:00 +0200</pubDate>
    <dc:creator>hr</dc:creator>
        <category>mobile</category>
        <category>e61i</category><category>e65</category><category>nokia</category><category>s60</category><category>symbian</category><category>sécurité</category>    
    <description>    &lt;p&gt;Je viens de découvrir une option assez sympathique qui se trouve, je crois, dans les S60 (vérifié sur un nokia e61i et un e65). Il est possible de bloquer son téléphone à distance grâce à un SMS en cas de perte. L'option se configure dans &lt;code&gt;menu &amp;gt; Outils &amp;gt; Paramètres &amp;gt; Sécurité &amp;gt; Téléphone et carte SIM &amp;gt; Autoriser verrouillage distant&lt;/code&gt;. A partir de là, on définit un message qui, lorsque reçu par SMS, va provoquer le bloquage du téléphone. Un autre code, à taper sur le téléphone, permet de le déverouiller. Plutôt cool je trouve.
&lt;img src=&quot;http://www.bonz.org/tech/public/tech/mobile/e61i_sms_lock.jpg&quot; alt=&quot;e61i SMS lock&quot; style=&quot;display:block; margin:0 auto;&quot; /&gt;&lt;/p&gt;</description>
    
    
    
          <comments>http://www.bonz.org/tech/post/2008/04/29/Je-te-casse-et-tu-ne-peux-rien-y-faire#comment-form</comments>
      <wfw:comment>http://www.bonz.org/tech/post/2008/04/29/Je-te-casse-et-tu-ne-peux-rien-y-faire#comment-form</wfw:comment>
      <wfw:commentRss>http://www.bonz.org/tech/feed/rss2/comments/135</wfw:commentRss>
      </item>
    
  <item>
    <title>clic clac kodak</title>
    <link>http://www.bonz.org/tech/post/2008/04/19/clic-clac-kodak</link>
    <guid isPermaLink="false">urn:md5:cac85e302145eaacab1498e87a0cdce7</guid>
    <pubDate>Sat, 19 Apr 2008 19:56:00 +0200</pubDate>
    <dc:creator>hr</dc:creator>
        <category>mobile</category>
        <category>e61i</category><category>nokia</category><category>s60</category><category>symbian</category>    
    <description>    &lt;p&gt;Mon premier réflexe a été de trouver un logiciel me permettant de faire des captures d'écran de mon e61i pour agrémenter les billets le concernant. J'ai trouvé un logiciel s'appelant tout simplement &lt;a href=&quot;http://www.antonypranata.com/screenshot&quot;&gt;screenshot&lt;/a&gt;, ayant le bon goût d'être sous license GPL. &lt;a href=&quot;http://www.bonz.org/tech/post/2008/04/18/In-the-pocket&quot;&gt;Le billet précédent&lt;/a&gt; est illustré par une de ces captures.&lt;/p&gt;</description>
    
    
    
          <comments>http://www.bonz.org/tech/post/2008/04/19/clic-clac-kodak#comment-form</comments>
      <wfw:comment>http://www.bonz.org/tech/post/2008/04/19/clic-clac-kodak#comment-form</wfw:comment>
      <wfw:commentRss>http://www.bonz.org/tech/feed/rss2/comments/134</wfw:commentRss>
      </item>
    
  <item>
    <title>In the pocket</title>
    <link>http://www.bonz.org/tech/post/2008/04/18/In-the-pocket</link>
    <guid isPermaLink="false">urn:md5:93ec4134903351a6d08da7393a255f5b</guid>
    <pubDate>Fri, 18 Apr 2008 12:22:00 +0200</pubDate>
    <dc:creator>hr</dc:creator>
        <category>mobile</category>
        <category>e61i</category><category>nokia</category><category>s60</category><category>symbian</category>    
    <description>    &lt;p&gt;Je me suis récemment acheté un &lt;a href=&quot;http://www.nokia.fr/link?cid=PLAIN_TEXT_907780&quot;&gt;nokia e61i&lt;/a&gt; (récemment chez moi c'est moins de 6 mois ... *kh*). Ce choix, en pleine folie iphone, repose sur différents critères&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;véritable clavier (azerty)&lt;/li&gt;
&lt;li&gt;3G (haha ... mais non c'est une fonction de l'iphone)&lt;/li&gt;
&lt;li&gt;symbian (pas de windows mobile)&lt;/li&gt;
&lt;li&gt;grand écran&lt;/li&gt;
&lt;li&gt;wifi&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ce téléphone est associé à un abonnement 3G+ illimité (il me manque le '+' ::/) qui devrait me permettre de rester connecté au fabuleux interweb.&lt;/p&gt;


&lt;p&gt;La première application que j'ai installée sur ce téléphone est &lt;a href=&quot;http://www.antonypranata.com/screenshot&quot;&gt;screenshot&lt;/a&gt; qui me permettra de prendre de jolies photos de mon téléphone.
&lt;img src=&quot;http://www.bonz.org/tech/public/tech/mobile/e61i_desktop01.jpg&quot; alt=&quot;e61i bureau&quot; style=&quot;display:block; margin:0 auto;&quot; /&gt;&lt;/p&gt;</description>
    
    
    
          <comments>http://www.bonz.org/tech/post/2008/04/18/In-the-pocket#comment-form</comments>
      <wfw:comment>http://www.bonz.org/tech/post/2008/04/18/In-the-pocket#comment-form</wfw:comment>
      <wfw:commentRss>http://www.bonz.org/tech/feed/rss2/comments/133</wfw:commentRss>
      </item>
    
  <item>
    <title>Sauvez du papier, mangez OpenOffice.org</title>
    <link>http://www.bonz.org/tech/post/2008/04/09/Sauvez-du-papier-mangez-OpenOfficeorg</link>
    <guid isPermaLink="false">urn:md5:c1073334099b40f255d1e2bf2c80aa76</guid>
    <pubDate>Wed, 09 Apr 2008 18:48:00 +0200</pubDate>
    <dc:creator>hr</dc:creator>
        <category>truc</category>
        <category>impression</category><category>openoffice</category><category>openoffice 2.3</category><category>openoffice 2.4</category><category>écologie</category>    
    <description>&lt;p&gt;En réponse à une préoccupation sur la consommation de papier lors de l'impression de documents, je me demande souvent comment imprimer 2 pages par feuille dans OpenOffice.org.&lt;/p&gt;    &lt;p&gt;Il faut de tout évidence commencer par ouvrir le document à imprimer. Ensuite, accéder à l'aperçu avant impression via &lt;code&gt;Fichier &amp;gt; Aperçu&lt;/code&gt;. Dans l'aperçu il y a un bouton nommé &quot;Options d'impression; aperçu&quot; à gauche du bouton &quot;Fermer l'aperçu&quot;. Ce bouton donne accès à une interface de préférences d'impression pour l'aperçu, en choisissant simplement &lt;code&gt;Format &amp;gt; Paysage&lt;/code&gt; et en conservant le nombre de colonnes à 2 on obtient le résultat recherché.
&lt;a href=&quot;http://www.bonz.org/tech/public/tech/2pages1feuille.png&quot;&gt;&lt;img src=&quot;http://www.bonz.org/tech/public/tech/.2pages1feuille_s.jpg&quot; alt=&quot;2 pages sur 1 feuille&quot; style=&quot;display:block; margin:0 auto;&quot; /&gt;&lt;/a&gt;
Il suffit maintenant d'imprimer l'aperçu en utilisant le bouton à gauche de &quot;Options d'impression: aperçu&quot; utilisé précédemment.&lt;/p&gt;</description>
    
    
    
          <comments>http://www.bonz.org/tech/post/2008/04/09/Sauvez-du-papier-mangez-OpenOfficeorg#comment-form</comments>
      <wfw:comment>http://www.bonz.org/tech/post/2008/04/09/Sauvez-du-papier-mangez-OpenOfficeorg#comment-form</wfw:comment>
      <wfw:commentRss>http://www.bonz.org/tech/feed/rss2/comments/129</wfw:commentRss>
      </item>
    
  <item>
    <title>Qui est le plus fort entre l'éléphant et l'hippopotame</title>
    <link>http://www.bonz.org/tech/post/2008/04/01/Qui-est-le-plus-fort-entre-lelephant-et-lhippopotame</link>
    <guid isPermaLink="false">urn:md5:680d04d6d6bc409ad11e407ba1a7f22f</guid>
    <pubDate>Wed, 02 Apr 2008 15:31:00 +0200</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>performance</category><category>python</category><category>regexp</category><category>timeit</category>    
    <description>&lt;p&gt;Je me pose toujours des questions de performance lorsque je développe. Il y a souvent plusieurs façons de rêgler un problème et certaines plus consommatrices de ressources que d'autres. Dans le cas qui m'intéresse, je me questionne sur la performance des expressions régulières en regard d'autres méthodes de traitement de chaîne. Je dois effectuer un traitement sur un très grand nombre de lignes de log au fur et à mesure de leur émission en &lt;code&gt;syslog&lt;/code&gt;.&lt;/p&gt;    &lt;p&gt;La plupart du temps avec python, avoir une idée innovante est modérée par l'existence d'un module qui couvre déjà le sujet. Dans ce cas, le module &lt;code&gt;&lt;a href=&quot;http://docs.python.org/lib/module-timeit.html&quot; hreflang=&quot;en&quot;&gt;timeit&lt;/a&gt;&lt;/code&gt; va me permettre de tester la rapidité d'exécution de différents bouts de code.&lt;/p&gt;


&lt;p&gt;Le format d'une ligne de log syslog est connu et décrit par la &lt;a href=&quot;http://www.ietf.org/rfc/rfc3164.txt&quot; hreflang=&quot;en&quot;&gt;RFC3164&lt;/a&gt;, ce format me permet différentes approches pour en extraire différents champs. Dans mon cas, je vais extraire le champ appelé &lt;code&gt;HEADER&lt;/code&gt; de différentes façons. Un ligne de log syslog doit être de la forme&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;     &amp;gt;TIMESTAMP HOSTNAME TAG CONTENT
| PRI |      HEADER      |    MSG
&lt;/pre&gt;

&lt;p&gt;Dans le code python ci-dessous, les trois fonctions testées sont&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;split()&lt;/code&gt; qui récupère le champ header en effectuant un split sur le caractère espace et en réunissant les champs résultants.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;match()&lt;/code&gt; effectue une sélection par expression régulière.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;match_full()&lt;/code&gt; effectue une capture par expression régulière et occurence nommée.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Il est important de bien faire attention au contexte et à la portée des variables et fonctions (&quot;scope&quot; et &quot;namespace&quot;) lorsqu'on utilise &lt;code&gt;timeit&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;
import timeit

def split():
    str = '&amp;lt;22&amp;gt;Mar 27 16:13:35 192.168.1.1 mx-daemon[2754]: m2RFDTbw002754: from=&amp;lt;j.doe@nowhere.com&amp;gt;, size=3356, class=0, nrcpts=1, msgid=&amp;lt;000701c8902d$04cb7356$879fa6b4@epwlhnj&amp;gt;, proto=ESMTP, daemon=MX, relay=[192.168.1.2]'
    ' '.join(str.split()[0:4]).split('&amp;gt;')[1]

def match():
    import re
    pattern = r'^&amp;lt;(\d+)&amp;gt;(\w{3} (?: |\d)\d \d\d:\d\d:\d\d [\w:.]+) (\w+.+)'
    _pattern = re.compile(pattern)
    str = '&amp;lt;22&amp;gt;Mar 27 16:13:35 192.168.1.1 mx-daemon[2754]: m2RFDTbw002754: from=&amp;lt;j.doe@nowhere.com&amp;gt;, size=3356, class=0, nrcpts=1, msgid=&amp;lt;000701c8902d$04cb7356$879fa6b4@epwlhnj&amp;gt;, proto=ESMTP, daemon=MX, relay=[192.168.1.2]'
    _pattern.search(str).group(2)

def match_full():
    import re
    pattern_full = r'^&amp;lt;(?P&amp;lt;pri&amp;gt;\d+)&amp;gt;(?P&amp;lt;header&amp;gt;\w{3} (?: |\d)\d \d\d:\d\d:\d\d [\w:.]+) (?P&amp;lt;msg&amp;gt;\w+.+)'
    _pattern_full = re.compile(pattern_full)
    str = '&amp;lt;22&amp;gt;Mar 27 16:13:35 192.168.1.1 mx-daemon[2754]: m2RFDTbw002754: from=&amp;lt;j.doe@nowhere.com&amp;gt;, size=3356, class=0, nrcpts=1, msgid=&amp;lt;000701c8902d$04cb7356$879fa6b4@epwlhnj&amp;gt;, proto=ESMTP, daemon=MX, relay=[192.168.1.2]'
    _pattern_full.search(str).groupdict()['header']

print &amp;quot;split:&amp;quot;
print timeit.Timer(&amp;quot;split()&amp;quot;, &amp;quot;from __main__ import split&amp;quot;).timeit()
print &amp;quot;match:&amp;quot;
print timeit.Timer(&amp;quot;match()&amp;quot;, &amp;quot;from __main__ import match&amp;quot;).timeit()
print &amp;quot;match_full:&amp;quot;
print timeit.Timer(&amp;quot;match_full()&amp;quot;, &amp;quot;from __main__ import match_full&amp;quot;).timeit()
&lt;/pre&gt;

&lt;p&gt;Ce qui donne après exécution&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
$ python stress.py
split:
6.14724898338
match:
14.80702281
match_full:
19.0696430206
&lt;/pre&gt;


&lt;p&gt;En y réfléchissant bien, les fonctions testées ne sont pas vraiment optimisées et ne sont pas donc comparables en vitesse d'exécution. Pour vraiment jouer sur un pied d'égalité, je procède aux modifications suivantes&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Import du module re en début de script, ça fonctionne finalement, j'avais peur que les problèmes de contexte de &lt;code&gt;timeit&lt;/code&gt; ne posent problème.&lt;/li&gt;
&lt;li&gt;La compilation des expressions régulières est faite une fois pour toute et pas à chaque lancement des fonctions &lt;code&gt;cmatch()&lt;/code&gt; et &lt;code&gt;cmatch_full()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Introduction de deux nouvelles fonctions effectuant une capture par expression régulière sans compilation de l'expression.&lt;/li&gt;
&lt;li&gt;Utilisation de la méthode &lt;code&gt;repeat()&lt;/code&gt; plutôt que &lt;code&gt;timeit()&lt;/code&gt; pour essayer d'obtenir un résultat moins influencé par le fonctionnement du système.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
import re, timeit

str='&amp;lt;22&amp;gt;Mar 27 16:13:35 192.168.1.1 mx-daemon[2754]: m2RFDTbw002754: from=&amp;lt;j.doe@nowhere.com&amp;gt;, size=3356, class=0, nrcpts=1, msgid=&amp;lt;000701c8902d$04cb7356$879fa6b4@epwlhnj&amp;gt;, proto=ESMTP, daemon=MX, relay=[192.168.1.2]'

split_init = &amp;quot;&amp;quot;&amp;quot;from __main__ import split&amp;quot;&amp;quot;&amp;quot;
match_init = &amp;quot;&amp;quot;&amp;quot;from __main__ import match&amp;quot;&amp;quot;&amp;quot;
match_full_init = &amp;quot;&amp;quot;&amp;quot;from __main__ import match_full&amp;quot;&amp;quot;&amp;quot;
cmatch_init = &amp;quot;&amp;quot;&amp;quot;from __main__ import cmatch&amp;quot;&amp;quot;&amp;quot;
cmatch_full_init = &amp;quot;&amp;quot;&amp;quot;from __main__ import cmatch_full&amp;quot;&amp;quot;&amp;quot;

pattern = r'^&amp;lt;(\d+)&amp;gt;(\w{3} (?: |\d)\d \d\d:\d\d:\d\d [\w:.]+) (\w+.+)'
pattern_full = r'^&amp;lt;(?P&amp;lt;pri&amp;gt;\d+)&amp;gt;(?P&amp;lt;header&amp;gt;\w{3} (?: |\d)\d \d\d:\d\d:\d\d [\w:.]+) (?P&amp;lt;msg&amp;gt;\w+.+)'
_pattern = re.compile(pattern)
_pattern_full = re.compile(pattern_full)

def split():
    ' '.join(str.split(' ')[0:4]).split('&amp;gt;')[1]

def match():
    re.search(pattern, str).group(2)

def match_full():
    re.search(pattern_full, str).groupdict()['header']

def cmatch():
    _pattern.search(str).group(2)

def cmatch_full():
    _pattern_full.search(str).groupdict()['header']

print &amp;quot;Testing %d occurences of each match, %d times&amp;quot; % (timeit.default_number, timeit.default_repeat)
split_result = timeit.Timer(&amp;quot;split()&amp;quot;, split_init).repeat()
print &amp;quot;split: %fs %s&amp;quot; % (min(split_result), split_result)
match_result = timeit.Timer(&amp;quot;match()&amp;quot;, match_init).repeat()
print &amp;quot;match: %fs %s&amp;quot; % (min(match_result), match_result)
match_full_result = timeit.Timer(&amp;quot;match_full()&amp;quot;, match_full_init).repeat()
print &amp;quot;match_full: %fs %s&amp;quot; % (min(match_full_result), match_full_result)
cmatch_result = timeit.Timer(&amp;quot;cmatch()&amp;quot;, cmatch_init).repeat()
print &amp;quot;cmatch: %fs %s&amp;quot; % (min(cmatch_result), cmatch_result)
cmatch_full_result = timeit.Timer(&amp;quot;cmatch_full()&amp;quot;, cmatch_full_init).repeat()
print &amp;quot;cmatch_full: %fs %s&amp;quot; % (min(cmatch_full_result), cmatch_full_result)
&lt;/pre&gt;


&lt;p&gt;Qui me donne les résultats suivants&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
$ python stress.py
Testing 1000000 occurences of each match, 3 times
split: 5.818318s [5.8183181285858154, 6.5981872081756592, 6.327794075012207]
match: 8.220056s [8.2262570858001709, 8.2361149787902832, 8.2200560569763184]
match_full: 12.399510s [12.40582799911499, 12.399509906768799, 12.501888036727905]
cmatch: 3.818037s [3.8180370330810547, 3.8276369571685791, 3.8310809135437012]
cmatch_full: 6.724747s [6.7247469425201416, 6.7392361164093018, 6.7564170360565186]
&lt;/pre&gt;

&lt;p&gt;Il est surprenant de constater la différence énorme entre les expressions régulières compilées et non compilées, l'avantage de compiler ses expressions dans le cas d'une utilisation de masse est évident. La rapidité de &lt;code&gt;split()&lt;/code&gt; est plutôt décevante par rapport à l'utilisation des expressions régullières.&lt;/p&gt;


&lt;p&gt;Ces tests sont intéressant et donnent des indications de performance mais ne remplacent pas des tests complet de &lt;a href=&quot;http://docs.python.org/lib/profile.html&quot; hreflang=&quot;en&quot;&gt;profilage&lt;/a&gt;.&lt;/p&gt;</description>
    
    
    
          <comments>http://www.bonz.org/tech/post/2008/04/01/Qui-est-le-plus-fort-entre-lelephant-et-lhippopotame#comment-form</comments>
      <wfw:comment>http://www.bonz.org/tech/post/2008/04/01/Qui-est-le-plus-fort-entre-lelephant-et-lhippopotame#comment-form</wfw:comment>
      <wfw:commentRss>http://www.bonz.org/tech/feed/rss2/comments/128</wfw:commentRss>
      </item>
    
  <item>
    <title>C'est curieux chez les marins ce besoin de faire des phrases</title>
    <link>http://www.bonz.org/tech/post/2008/03/18/Cest-curieux-chez-les-marins-ce-besoin-de-faire-des-phrases</link>
    <guid isPermaLink="false">urn:md5:b67d011b6c4f2f48a14c8b2ac225f6c1</guid>
    <pubDate>Tue, 18 Mar 2008 15:47:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>c</category><category>sed</category>    
    <description>    &lt;p&gt;Suite à &lt;a href=&quot;http://www.bonz.org/tech/post/2008/03/17/Pour-en-finir-avec-Lulu-la-Nantaise#c543&quot; hreflang=&quot;fr&quot;&gt;une remarque judicieuse&lt;/a&gt; j'ai été obligé de gérer un petit cas particulier qui conduisait à un conflit. Le &lt;a href=&quot;http://www.bonz.org/files/ccmtstrip.sed&quot;&gt;script sed&lt;/a&gt; est maintenant modifié, ce n'est pas extrêmement propre mais c'est assez efficace. Le bout du script concerné est&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
# remove /* */ comments, multiline or not
/\/\*/{
    # special case of /*/ starting comments that fools the next test
    s:/\*/:/\* /:
    :mline
    /\*\// {
        s:/\*.*\*/::
        b mlineend
    }
    N
    b mline
}
:mlineend
&lt;/pre&gt;</description>
    
    
    
          <comments>http://www.bonz.org/tech/post/2008/03/18/Cest-curieux-chez-les-marins-ce-besoin-de-faire-des-phrases#comment-form</comments>
      <wfw:comment>http://www.bonz.org/tech/post/2008/03/18/Cest-curieux-chez-les-marins-ce-besoin-de-faire-des-phrases#comment-form</wfw:comment>
      <wfw:commentRss>http://www.bonz.org/tech/feed/rss2/comments/127</wfw:commentRss>
      </item>
    
  <item>
    <title>Pour en finir avec Lulu la Nantaise</title>
    <link>http://www.bonz.org/tech/post/2008/03/17/Pour-en-finir-avec-Lulu-la-Nantaise</link>
    <guid isPermaLink="false">urn:md5:cf7307e34e9c4d47776363b984d66d4d</guid>
    <pubDate>Mon, 17 Mar 2008 12:32:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>c</category><category>sed</category>    
    <description>&lt;p&gt;Au final, je me suis occupé des espaces, le but de ce script étant au départ de fournir un code nettoyé qui pourra passer au banc des statistiques. Le script sed est &lt;a href=&quot;http://www.bonz.org/files/ccmtstrip.sed&quot;&gt;disponible&lt;/a&gt; et placé sous license type *BSD. Quelques corrections d'erreur et de nouvelles fonctions constituent cette dernière version du script.&lt;/p&gt;    &lt;p&gt;J'utilise un petit fichier de définition &lt;code&gt;test.h&lt;/code&gt;&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
/*
 * Big story here
 * my life
 */
// one line
#define HEHE 1

/* includes */
#include &amp;quot;blah&amp;quot;
#include &amp;quot;foo&amp;quot; // end line comment

#include /* weird comment */ &amp;quot;pouet&amp;quot;
int woop /* to see */
/* let's continue with weird comments */ void sigpipe_handle(int);

/*-------------
  bloh
---------------*/
int main(int argc, char **argv);
int parseCommandLine(int argc, char* argv[]);
&lt;/pre&gt;

&lt;p&gt;Ce qui donne, une fois passé par sed&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
$ sed -nf ccmtstrip.sed test.h
#define HEHE 1
#include &amp;quot;blah&amp;quot;
#include &amp;quot;foo&amp;quot;
#include &amp;quot;pouet&amp;quot;
int woop
void sigpipe_handle(int);
int main(int argc, char **argv);
int parseCommandLine(int argc, char* argv[]);
&lt;/pre&gt;</description>
    
    
    
          <comments>http://www.bonz.org/tech/post/2008/03/17/Pour-en-finir-avec-Lulu-la-Nantaise#comment-form</comments>
      <wfw:comment>http://www.bonz.org/tech/post/2008/03/17/Pour-en-finir-avec-Lulu-la-Nantaise#comment-form</wfw:comment>
      <wfw:commentRss>http://www.bonz.org/tech/feed/rss2/comments/126</wfw:commentRss>
      </item>
    
  <item>
    <title>Les commentaires déssoudés par Jo le trembleur</title>
    <link>http://www.bonz.org/tech/post/2008/03/15/Les-commentaires-dessoudes-par-Jo-le-trembleur</link>
    <guid isPermaLink="false">urn:md5:9892afc0ad555608da23da5264809480</guid>
    <pubDate>Sat, 15 Mar 2008 21:21:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>c</category><category>sed</category>    
    <description>&lt;p&gt;La version &lt;a href=&quot;http://www.bonz.org/tech/post/2008/03/14/Du-C-par-Teddy-de-Montreal&quot; hreflang=&quot;fr&quot;&gt;précédente&lt;/a&gt; du script sed ne me convenait pas. Elle ne gérait pas certaines formes de commentaires horribles, qui mériterait une sentence extrême et douloureuse. C'est maintenant chose rêglée avec une version améliorée.&lt;/p&gt;    &lt;pre&gt;
#!/usr/bin/sed -nf

# remove single line comments
/^\/\//d

# remove /* */ comments, multiline or not
/\/\*/{
    :mline
    /\*\// {
        s:/\*.*\*/::
        b mlineend
    }
    N
    b mline
}
:mlineend

# remove empty lines
/^$/d

p
&lt;/pre&gt;

&lt;p&gt;Il peut arriver que certains espaces inutiles apparaissent dans le code suite à la suppression des commentaires multiligne mais ce n'est pas fondamentalement un problème.&lt;/p&gt;</description>
    
    
    
          <comments>http://www.bonz.org/tech/post/2008/03/15/Les-commentaires-dessoudes-par-Jo-le-trembleur#comment-form</comments>
      <wfw:comment>http://www.bonz.org/tech/post/2008/03/15/Les-commentaires-dessoudes-par-Jo-le-trembleur#comment-form</wfw:comment>
      <wfw:commentRss>http://www.bonz.org/tech/feed/rss2/comments/125</wfw:commentRss>
      </item>
    
  <item>
    <title>Du C par Teddy de Montreal</title>
    <link>http://www.bonz.org/tech/post/2008/03/14/Du-C-par-Teddy-de-Montreal</link>
    <guid isPermaLink="false">urn:md5:3e74dbfb5103e8942b71f2b23dbc3227</guid>
    <pubDate>Fri, 14 Mar 2008 17:25:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>c</category><category>sed</category>    
    <description>&lt;p&gt;&quot; ... un fondu qui travaillait qu'à la dynamite.&quot; Je me suis mis en tête de compter le nombre de lignes efficaces d'un développement en C. Par ligne efficace j'entends le nombre de lignes de code vraiment compilées. Je dois donc éliminer les commentaires sous leurs diverses formes et les lignes blanches. Et comme j'aime bien me faire mal, au lieu de faire ça en 10 minutes en Perl ou Python, je l'ai fait grâce à &lt;code&gt;sed(1)&lt;/code&gt;.&lt;/p&gt;    &lt;p&gt;Ce script sed doit donc gérer les lignes blanches, les commentaires sur une ligne commençant pas '&lt;code&gt;//&lt;/code&gt;', les commentaires sur plusieurs lignes (ou pas) du type &lt;code&gt;/* comment */&lt;/code&gt;. Ca donne alors le script suivant&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
#!/bin/sed -nf

# remove empty lines
/^$/d
# remove single line comments
/^\/\//d

# remove /* */ comments, multiline or not 
/\/\*/{
    :mline
    /\*\//b mlineend
    N
    b mline
    :mlineend
    d
}
p
&lt;/pre&gt;

&lt;p&gt;Le seul passage qui me semble nécessiter une explication est celui qui gère les commentaires sur plusieurs lignes. Ce qui est fait là est assez simple (sisi c'est vrai)&amp;nbsp;: on ne traite que les lignes comprenant '&lt;code&gt;/*&lt;/code&gt;', si la ligne comprend aussi '&lt;code&gt;*/&lt;/code&gt;' on passe à la balise mlineend, sinon on ajoute la ligne suivante aux lignes déjà lues et on passe à la balise mline (boucle). Le saut à la balise mlineend provoque la destruction des lignes lues et le passage à la ligne suivante en recommençant le script.&lt;br /&gt;
On finit par afficher la ligne (grâce à 'p') si aucun des cas précédents ne s'est produit.&lt;/p&gt;


&lt;p&gt;Le défaut de ce script est de ne pas gérer les cas où les commentaires sont mixés avec du code. Je pense l'améliorer dans un deuxième temps pour prendre en compte ces cas.&lt;/p&gt;


&lt;p&gt;&lt;ins&gt;&lt;strong&gt;Mise à jour&lt;/strong&gt;&lt;/ins&gt;: une &lt;a href=&quot;http://www.bonz.org/tech/post/2008/03/15/Les-commentaires-dessoudes-par-Jo-le-trembleur&quot; hreflang=&quot;fr&quot;&gt;nouvelle version&lt;/a&gt; de ce script règle ces soucis.&lt;/p&gt;</description>
    
    
    
          <comments>http://www.bonz.org/tech/post/2008/03/14/Du-C-par-Teddy-de-Montreal#comment-form</comments>
      <wfw:comment>http://www.bonz.org/tech/post/2008/03/14/Du-C-par-Teddy-de-Montreal#comment-form</wfw:comment>
      <wfw:commentRss>http://www.bonz.org/tech/feed/rss2/comments/124</wfw:commentRss>
      </item>
    
  <item>
    <title>Booooooonjouuuuuuuuuuuur jeee m'aaaappeeeeeeeeeeeeelle Dooooooooriiiiiiiiiiiis</title>
    <link>http://www.bonz.org/tech/post/2008/03/12/Booooooonjouuuuuuuuuuuur-jeee-maaaappeeeeeeeeeeeeelle-Dooooooooriiiiiiiiiiiis</link>
    <guid isPermaLink="false">urn:md5:3b3acdda0ae236ebe8705160e7ffb8e8</guid>
    <pubDate>Thu, 13 Mar 2008 15:54:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>bash</category><category>snmp</category>    
    <description>&lt;p&gt;Suite à la configuration de net-snmp pour &lt;a href=&quot;http://www.bonz.org/tech/post/2008/03/04/All-your-poll-are-belong-to-us&quot; hreflang=&quot;fr&quot;&gt;utiliser des scripts&lt;/a&gt;, il a bien fallu coder un compteur 32bits. Aimant les défis, je me suis décidé à écrire un compteur 32 bits en bash qui compte les entrées d'une base de donnée.&lt;/p&gt;    &lt;p&gt;Comment doit fonctionner un compteur 32 bits en script&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Le compteur est un entier modulo 2^32&lt;/li&gt;
&lt;li&gt;Le compteur augmente uniquement de la différence de valeur entre deux requêtes&lt;/li&gt;
&lt;li&gt;Le compteur doit être conservé entre deux requêtes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pour implémenter le modulo, ce n'est pas très compliqué en &lt;code&gt;bash/sh&lt;/code&gt;&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
$(($count % 2**32))
&lt;/pre&gt;

&lt;p&gt;J'ai choisi de stocker les données dans un &lt;a href=&quot;http://tldp.org/LDP/abs/html/arrays.html&quot; hreflang=&quot;en&quot;&gt;tableau bash&lt;/a&gt;. Pour conserver les données entre 2 appels, je stocke directement le tableau dans un fichier sous la forme d'une définition de tableau bash. De cette façon, je peux inclure le fichier au début de mon script et j'évite d'avoir à lire un fichier de donnée. Les données que je conserve sont le résultat de ma dernière requête SQL, la différence entre la dernière requête et la précédente, et finalement mon compteur. Pourquoi conserver le dernier résultat&amp;nbsp;? Ca me permet au passage de choisir le comportement du compteur entre compteur 32 bits et donnée directe (gauge).&lt;br /&gt;
La requête SQL est tout simplement un &lt;code&gt;count(*)&lt;/code&gt; sur une table, le nombre de lignes de cette table peut augmenter ou diminuer (si il ne faisait qu'augmenter, je pourrais directement prendre le &lt;code&gt;count(*)&lt;/code&gt; modulo 2^32).&lt;/p&gt;
&lt;pre&gt;
#!/bin/sh

counters='pgcount.txt'
# call the counters
if [ -f $counters ]; then
    . $counters
else
    # last count / last diff / counter
    count=(0 0 0)
fi

# adds 2 values
# return the sum modulo 2**32
counter() {
    counter=$(($1 + $2))
    echo -n $(($counter % 2**32))
}

# dumps the values as bash arrays
dump() {
    echo &amp;quot;count=(${count[@]})&amp;quot; &amp;gt; $counters
}

getdata() {
    count=($@)
    rows=$(getcount $table)
    if [ &amp;quot;${count[1]}&amp;quot; -ne &amp;quot;$count&amp;quot; ]; then
        count[1]=$(($rows - ${count[0]}))
        count[0]=$rows
        count[2]=$(counter ${count[2]} ${count[1]})
    fi
    # return needed values
    echo ${count[@]}
}

count=($(getdata ${count[@]}))
dump()
&lt;/pre&gt;

&lt;p&gt;La fonction &lt;code&gt;getcount()&lt;/code&gt; effectue le &lt;code&gt;count(*)&lt;/code&gt; et retourne le nombre de lignes lues.&lt;/p&gt;


&lt;p&gt;Ce script est lancé toutes les minutes car la requêtes peut parfois prendre du temps et provoquer un timeout snmp. Mon serveur net-snmp appelle donc un bête script qui inclut le fichier de sauvegarde et renvoit les données sauvées.&lt;/p&gt;
&lt;pre&gt;
#!/bin/sh

counters='pgcount.txt'
# call the counters
if [ -f $counters ]; then
    . $counters
else
    # last count / last diff / counter
    count=(0 0 0)
fi

echo ${count[1]}
echo ${count[2]}
&lt;/pre&gt;

&lt;p&gt;En sélectionnant la donnée que je veux dans mon chemin snmp je peux obtenir le nombre de lignes insérées sous forme de compteur 32 bits ou le nombre de lignes insérées entre deux exécutions du scipt. Cette dernière valeur est bancale puisqu'elle ne représente pas le nombre de lignes insérées entre deux consultations du compteur. Il serait possible de le faire fonctionner correctement si le script de lecture réécrivait le fichier de sauvegarde à chaque lecture pour signifier qu'il a été consulté. Il faudrait alors implémenter un verrou sur ce fichier pour éviter qu'il soit utilisé par les 2 scripts en même temps.&lt;/p&gt;


&lt;p&gt;&lt;ins&gt;NB&lt;/ins&gt;: Désolé pour le titre du post, je ne trouvais rien de plus inspiré mais un compteur 32bits c'est un peu comme &lt;a href=&quot;http://fr.wikipedia.org/wiki/Le_Monde_de_Nemo&quot; hreflang=&quot;fr&quot;&gt;Doris&lt;/a&gt;, au bout d'un certain temps, il revient à zero. Oui, je sais ...&lt;/p&gt;</description>
    
    
    
          <comments>http://www.bonz.org/tech/post/2008/03/12/Booooooonjouuuuuuuuuuuur-jeee-maaaappeeeeeeeeeeeeelle-Dooooooooriiiiiiiiiiiis#comment-form</comments>
      <wfw:comment>http://www.bonz.org/tech/post/2008/03/12/Booooooonjouuuuuuuuuuuur-jeee-maaaappeeeeeeeeeeeeelle-Dooooooooriiiiiiiiiiiis#comment-form</wfw:comment>
      <wfw:commentRss>http://www.bonz.org/tech/feed/rss2/comments/122</wfw:commentRss>
      </item>
    
  <item>
    <title>All your poll are belong to us</title>
    <link>http://www.bonz.org/tech/post/2008/03/04/All-your-poll-are-belong-to-us</link>
    <guid isPermaLink="false">urn:md5:478d1667209ca96937554b17ebdbc7f8</guid>
    <pubDate>Wed, 05 Mar 2008 18:36:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>système</category>
        <category>debian</category><category>etch</category><category>net-snmp</category><category>snmp</category>    
    <description>&lt;p&gt;J'ai aujourd'hui besoin de tenir à l'oeil certaines valeurs sur un serveur, le plus simple semble d'utiliser &lt;a href=&quot;http://net-snmp.sourceforge.net/&quot; hreflang=&quot;en&quot;&gt;net-snmp&lt;/a&gt; et &lt;a href=&quot;http://oss.oetiker.ch/mrtg/&quot; hreflang=&quot;en&quot;&gt;mrtg&lt;/a&gt;. Ce serveur SNMP peut être très facilement étendu, mais est assez mal documenté à mon goût. Je vais donc faire un petit tour des méthodes simples d'utilisation de net-snmp pour récupérer des données non communes.&lt;/p&gt;    &lt;p&gt;Les versions de net-snmp et mrtg installées sur ma debian etch sont &lt;code&gt;snmpd (5.2.3-7etch2)&lt;/code&gt; et &lt;code&gt;mrtg (2.14.7-2)&lt;/code&gt;. Je suppose que vous êtes à l'aise avec le concept de SNMP et que net-snmp et mrtg sont configurés.&lt;br /&gt;
En faisant le tour du site web, du fichier de configuration par défaut et de goo^Winternet, j'ai pû lister les méthodes suivantes&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;exec&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;extend&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pass&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je passe sur l'utilisation de &lt;a href=&quot;http://net-snmp.sourceforge.net/wiki/index.php/TUT:Writing_a_Subagent&quot; hreflang=&quot;en&quot;&gt;subagent&lt;/a&gt; qui est plus complexe.&lt;br /&gt;
Le script que je vais utiliser tient en 2 lignes et se trouve dans &lt;code&gt;/tmp/test_snmp&lt;/code&gt;&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;#!/bin/sh
echo 42
&lt;/pre&gt;

&lt;p&gt;Pour information, le fichier &lt;code&gt;/usr/share/snmp/mibs/UCD-SNMP-MIB.txt&lt;/code&gt; indique que l'OID 2021 correspond à &lt;code&gt;ucdavis&lt;/code&gt; et se trouve après le noeud &lt;code&gt;enterprise&lt;/code&gt; (ce qui donne &lt;code&gt;.1.3.6.1.4.1.2021&lt;/code&gt;). Cet OID sera utilisé pour ajouter de nouvelles données en test, en réalité il faudrait utiliser un OID correspondant à l'environnement d'utilisation (un OID d'entreprise délivré par &lt;a href=&quot;http://www.iana.org/assignments/enterprise-numbers&quot; hreflang=&quot;en&quot;&gt;IANA&lt;/a&gt; par exemple).&lt;/p&gt;


&lt;h2&gt;&lt;code&gt;exec&lt;/code&gt;&lt;/h2&gt;
&lt;pre&gt;
[...]
exec test1 /tmp/test_snmp
exec .1.3.6.1.4.1.2021.60 test2 /tmp/test_snmp
[...]
&lt;/pre&gt;

&lt;p&gt;Après redémarrage, on vérifie que les données sont bien accessibles en SNMP&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
$ snmpwalk -v 1 -c private localhost .1.3.6.1.4.1.2021
[...]
UCD-SNMP-MIB::extIndex.1 = INTEGER: 1
UCD-SNMP-MIB::extNames.1 = STRING: test_snmp
UCD-SNMP-MIB::extCommand.1 = STRING: /tmp/test_snmp
UCD-SNMP-MIB::extResult.1 = INTEGER: 0
UCD-SNMP-MIB::extOutput.1 = STRING: 42
UCD-SNMP-MIB::extErrFix.1 = INTEGER: 0
UCD-SNMP-MIB::extErrFixCmd.1 = STRING:
[...]
UCD-SNMP-MIB::ucdavis.60.1.1 = INTEGER: 1
UCD-SNMP-MIB::ucdavis.60.2.1 = STRING: &amp;quot;test_snmp&amp;quot;
UCD-SNMP-MIB::ucdavis.60.3.1 = STRING: &amp;quot;/tmp/test_snmp&amp;quot;
UCD-SNMP-MIB::ucdavis.60.100.1 = INTEGER: 0
UCD-SNMP-MIB::ucdavis.60.101.1 = STRING: &amp;quot;42&amp;quot;
UCD-SNMP-MIB::ucdavis.60.102.1 = INTEGER: 0
UCD-SNMP-MIB::ucdavis.60.103.1 = &amp;quot;&amp;quot;
[...]
&lt;/pre&gt;

&lt;p&gt;La première forme est plus explicite et la deuxième forme est mieux maitrisée puisqu'on choisit l'OID destination.&lt;/p&gt;


&lt;p&gt;Le problème est que le serveur snmp n'est pas très satisfait de l'utilisation de &lt;code&gt;exec&lt;/code&gt;&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
[...]
Mar  4 17:28:45 snmp.priv snmpd[8678]: /etc/snmp/snmpd.conf: line 363: Error: Warning: relocatable 'exec' format will change in a future release - See 'NET-SNMP-EXTEND-MIB' for an alternative
Mar  4 17:28:45 snmp.priv snmpd[8678]: net-snmp: 2 error(s) in config file(s)
&lt;/pre&gt;


&lt;h2&gt;&lt;code&gt;extend&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;L'uilisation de &lt;code&gt;extend&lt;/code&gt; se fait via une mib spéciale (&lt;code&gt;NET-SNMP-EXTEND-MIB.txt&lt;/code&gt;), sur une debian elle se trouve dans &lt;code&gt;/usr/share/snmp/mibs&lt;/code&gt;. L'utilisation de cette option permet de créer automatiquement des entrées sous l'OID &lt;code&gt;.1.3.6.1.4.1.8072&lt;/code&gt; par défaut.&lt;/p&gt;
&lt;pre&gt;
extend test_snmp /tmp/test_snmp
extend .1.3.6.1.4.1.2021.60 test_snmp /tmp/test_snmp
&lt;/pre&gt;

&lt;p&gt;Les résultats se trouvent aux 2 endroits suivants&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
$ snmpwalk -v 1 -c private localhost NET-SNMP-EXTEND-MIB::nsExtendObjects
[...]
NET-SNMP-EXTEND-MIB::nsExtendNumEntries.0 = INTEGER: 3
NET-SNMP-EXTEND-MIB::nsExtendCommand.&amp;quot;test_snmp&amp;quot; = STRING: /tmp/test_snmp
NET-SNMP-EXTEND-MIB::nsExtendArgs.&amp;quot;test_snmp&amp;quot; = STRING: 
NET-SNMP-EXTEND-MIB::nsExtendInput.&amp;quot;test_snmp&amp;quot; = STRING: 
NET-SNMP-EXTEND-MIB::nsExtendCacheTime.&amp;quot;test_snmp&amp;quot; = INTEGER: 5
NET-SNMP-EXTEND-MIB::nsExtendExecType.&amp;quot;test_snmp&amp;quot; = INTEGER: exec(1)
NET-SNMP-EXTEND-MIB::nsExtendRunType.&amp;quot;test_snmp&amp;quot; = INTEGER: run-on-read(1)
NET-SNMP-EXTEND-MIB::nsExtendStorage.&amp;quot;test_snmp&amp;quot; = INTEGER: permanent(4)
NET-SNMP-EXTEND-MIB::nsExtendStatus.&amp;quot;test_snmp&amp;quot; = INTEGER: active(1)
NET-SNMP-EXTEND-MIB::nsExtendOutput1Line.&amp;quot;test_snmp&amp;quot; = STRING: 42
NET-SNMP-EXTEND-MIB::nsExtendOutputFull.&amp;quot;test_snmp&amp;quot; = STRING: 42
NET-SNMP-EXTEND-MIB::nsExtendOutNumLines.&amp;quot;test_snmp&amp;quot; = INTEGER: 1
NET-SNMP-EXTEND-MIB::nsExtendResult.&amp;quot;test_snmp&amp;quot; = INTEGER: 0
NET-SNMP-EXTEND-MIB::nsExtendOutLine.&amp;quot;test_snmp&amp;quot;.1 = STRING: 42
[...]
$ snmpwalk -v 1 -c private localhost .1.3.6.1.4.1.2021.60
UCD-SNMP-MIB::ucdavis.60.1.0 = INTEGER: 1
UCD-SNMP-MIB::ucdavis.60.2.1.2.9.116.101.115.116.95.115.110.109.112 = STRING: &amp;quot;/tmp/test_snmp&amp;quot;
UCD-SNMP-MIB::ucdavis.60.2.1.3.9.116.101.115.116.95.115.110.109.112 = &amp;quot;&amp;quot;
UCD-SNMP-MIB::ucdavis.60.2.1.4.9.116.101.115.116.95.115.110.109.112 = &amp;quot;&amp;quot;
UCD-SNMP-MIB::ucdavis.60.2.1.5.9.116.101.115.116.95.115.110.109.112 = INTEGER: 5
UCD-SNMP-MIB::ucdavis.60.2.1.6.9.116.101.115.116.95.115.110.109.112 = INTEGER: 1
UCD-SNMP-MIB::ucdavis.60.2.1.7.9.116.101.115.116.95.115.110.109.112 = INTEGER: 1
UCD-SNMP-MIB::ucdavis.60.2.1.20.9.116.101.115.116.95.115.110.109.112 = INTEGER: 4
UCD-SNMP-MIB::ucdavis.60.2.1.21.9.116.101.115.116.95.115.110.109.112 = INTEGER: 1
UCD-SNMP-MIB::ucdavis.60.3.1.1.9.116.101.115.116.95.115.110.109.112 = STRING: &amp;quot;42&amp;quot;
UCD-SNMP-MIB::ucdavis.60.3.1.2.9.116.101.115.116.95.115.110.109.112 = STRING: &amp;quot;42&amp;quot;
UCD-SNMP-MIB::ucdavis.60.3.1.3.9.116.101.115.116.95.115.110.109.112 = INTEGER: 1
UCD-SNMP-MIB::ucdavis.60.3.1.4.9.116.101.115.116.95.115.110.109.112 = INTEGER: 0
UCD-SNMP-MIB::ucdavis.60.4.1.2.9.116.101.115.116.95.115.110.109.112.1 = STRING: &amp;quot;42&amp;quot;
&lt;/pre&gt;

&lt;p&gt;La deuxième forme revient à peu de choses près au résultat de la commande &lt;code&gt;exec&lt;/code&gt; avec utilisation d'un chemin.&lt;br /&gt;
La première forme permet d'obtenir beaucoup d'informations intéressantes comme le nombre d'éléments qui étendent la mib (&lt;code&gt;NET-SNMP-EXTEND-MIB::nsExtendNumEntries.0&lt;/code&gt; on remarque donc que j'ai nettoyé la réponse puisqu'il annonce 3 scripts d'extension). On peut récupérer la réponse complète &lt;code&gt;NET-SNMP-EXTEND-MIB::nsExtendOutputFull.&quot;test_snmp&quot;&lt;/code&gt; ou les lignes une à une &lt;code&gt;NET-SNMP-EXTEND-MIB::nsExtendOutLine.&quot;test_snmp&quot;.x&lt;/code&gt;.&lt;/p&gt;


&lt;p&gt;Le point faible de cette méthode subsiste dans le type de donnée renvoyé, notre script envoit un chiffre et il est considéré comme une chaine de caractère. De la même façon, si on veut générer un compteur sur 32 ou 64 bits, c'est impossible avec cette méthode.&lt;/p&gt;


&lt;h2&gt;&lt;code&gt;pass&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;L'utilisation de cette option de configuration oblige à choisir un OID en dessous duquel le script est entièrement maitre. Il faut donc avoir recours à un script plus complexe qui permet de donner le type de valeur et la valeur pour un OID donné. Le script &lt;code&gt;passtest&lt;/code&gt; donne une bonne idée d'un script basique permettant cette intégration. Je ne rentrerai pas dans les détails.&lt;/p&gt;
&lt;pre&gt;
pass .1.3.6.1.4.1.2021.60 /tmp/test_snmp
&lt;/pre&gt;

&lt;p&gt;Dans l'idée, le script est appelé avec les arguments &lt;code&gt;&amp;lt;reqtype&amp;gt;&lt;/code&gt; &lt;code&gt;&amp;lt;miboid&amp;gt;&lt;/code&gt;. Avec &lt;code&gt;reqtype&lt;/code&gt; qui correspond à &lt;code&gt;-g&lt;/code&gt; pour une requête &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;-n&lt;/code&gt; pour un &lt;code&gt;GETNEXT&lt;/code&gt; et un &lt;code&gt;-s&lt;/code&gt; pour un &lt;code&gt;SET&lt;/code&gt;. le chemin OID est donné par &lt;code&gt;miboid&lt;/code&gt;.&lt;br /&gt;
Dans le cas des commandes &lt;code&gt;GET&lt;/code&gt; et &lt;code&gt;GETNEXT&lt;/code&gt;, la réponse est constituée de 3 lignes&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;l'OID de réponse;&lt;/li&gt;
&lt;li&gt;le type de la valeur retournée, au choix dans integer, gauge, counter, timeticks, ipaddress, objectid, ou string;&lt;/li&gt;
&lt;li&gt;la valeur.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dans le cas de la commande &lt;code&gt;SET&lt;/code&gt;, le script ne doit rien retourner.&lt;/p&gt;


&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Il faut bien en finir avec tout ce bazar, il est intéressant de jeter un coup d'oeil du côté de &lt;code&gt;snmpd.conf(5)&lt;/code&gt; pour les détails, d'autres options de configuration comme &lt;code&gt;pass_persist&lt;/code&gt; peuvent être utilisées. Personnellement, j'utilise la solution &lt;code&gt;extend&lt;/code&gt; qui me semble raisonnablement simple à mettre en place et à utiliser.&lt;/p&gt;</description>
    
    
    
          <comments>http://www.bonz.org/tech/post/2008/03/04/All-your-poll-are-belong-to-us#comment-form</comments>
      <wfw:comment>http://www.bonz.org/tech/post/2008/03/04/All-your-poll-are-belong-to-us#comment-form</wfw:comment>
      <wfw:commentRss>http://www.bonz.org/tech/feed/rss2/comments/119</wfw:commentRss>
      </item>
    
  <item>
    <title>Le log qui se voulait plus gros que le boeuf</title>
    <link>http://www.bonz.org/tech/post/2008/02/18/Le-log-qui-se-voulait-plus-gros-que-le-boeuf</link>
    <guid isPermaLink="false">urn:md5:79ce7425673a4818129a484571ec379e</guid>
    <pubDate>Thu, 21 Feb 2008 17:52:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>système</category>
        <category>freebsd</category><category>lightppd 1.4</category><category>lighttpd</category>    
    <description>&lt;p&gt;C'est généralement au moment où on se retrouve avec une partition pleine que l'oubli de la rotation des logs refait surface. C'est ultra-classique et pourtant... Mais nous allons prendre les devants pour que lighty ne soit pas un mangeur d'espace.&lt;/p&gt;    &lt;p&gt;Sur &lt;a href=&quot;http://www.freebsd.org/&quot;&gt;FreeBSD&lt;/a&gt;, le gestionnaire de rotation de log de base s'appelle &lt;code&gt;newsyslog(8)&lt;/code&gt;, il est configuré par le fichier &lt;code&gt;/etc/newsyslog.conf&lt;/code&gt; (&lt;code&gt;newsyslog.conf(5)&lt;/code&gt;). Je suis habitué à &lt;code&gt;logrotate(8)&lt;/code&gt; et j'aime bien conserver tous mes fichiers de logs d'activité web, je vais donc faire la même chose avec &lt;code&gt;newsyslog(5)&lt;/code&gt;. Je souhaite donc faire tourner mes logs tous les jours à minuit, compresser les logs passés et les conserver &lt;em&gt;ad vitam eternam&lt;/em&gt;. Ces contraintes donnent l'entrée suivante dans le fichier de configuration&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
# lighttpd rotates
/var/log/lighttpd/lighttpd.access.log   www:www   640  366 *   @T00   JC
&lt;/pre&gt;

&lt;p&gt;il faut spécifier un champ de gestion d'appartenance (propriétaire et groupe), lighttpd écrit les logs avec les droits www:www et ils seront déplacés en changeant le propriétaire en root:www.Les fichiers seront créés avec les droits &lt;code&gt;640&lt;/code&gt;, le champ suivant spécifie que les fichiers sont sauvegardés 366 fois avant d'éliminer les fichiers les plus anciens (1 an). Les 2 champs suivant provoquent une rotation à minuit tous les jours. Le dernier champ précise qu'on compresse les archive de log en bzip2 et que le fichier est créé si il n'existe pas.&lt;br /&gt;
Le champ 'count' n'est pas exhaustivement décrit dans la page de man, et j'espérais pouvoir utiliser une valeur qui me permettrait d'avoir un nombre infini de fichiers de logs. La lecture du &lt;a href=&quot;http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.sbin/newsyslog/newsyslog.c?rev=1.108&quot;&gt;code source&lt;/a&gt; m'a confirmé que je ne pouvais pas utiliser le même type de fonctionnement que pour &lt;code&gt;logrotate(8)&lt;/code&gt;.&lt;/p&gt;


&lt;p&gt;Une entrée doit être créée pour chaque fichier de log de chaque vhost.&lt;/p&gt;</description>
    
    
    
          <comments>http://www.bonz.org/tech/post/2008/02/18/Le-log-qui-se-voulait-plus-gros-que-le-boeuf#comment-form</comments>
      <wfw:comment>http://www.bonz.org/tech/post/2008/02/18/Le-log-qui-se-voulait-plus-gros-que-le-boeuf#comment-form</wfw:comment>
      <wfw:commentRss>http://www.bonz.org/tech/feed/rss2/comments/118</wfw:commentRss>
      </item>
    
  <item>
    <title>Un blog bien sous tout rapport</title>
    <link>http://www.bonz.org/tech/post/2008/02/14/Un-blog-bien-sous-tout-rapport</link>
    <guid isPermaLink="false">urn:md5:0389153de5dab650769cc255f2f8d6cb</guid>
    <pubDate>Thu, 14 Feb 2008 16:39:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>système</category>
        <category>dotclear 2</category><category>freebsd</category><category>lighttpd</category><category>lighttpd 1.4</category>    
    <description>&lt;p&gt;Ce blog est un &lt;a href=&quot;http://dev.dotclear.net/2.0/&quot;&gt;dotclear 2&lt;/a&gt; installé sur un système &lt;a href=&quot;http://www.freebsd.org/&quot;&gt;FreeBSD&lt;/a&gt; et servi par un &lt;a href=&quot;http://www.lighttpd.net/&quot;&gt;lighty&lt;/a&gt;. J'ai choisi dotclear pour la possibilité de gérer de multiples blogs à partir d'un seul code, &lt;a href=&quot;http://www.wordpress.com/&quot;&gt;wordpress&lt;/a&gt; ne donnait pas cette possibilité simplement au moment de mon choix. Mon premier déploiement n'a pas été fait dans les règles FreeBSD, il est temps de remédier à cet insupportable état de fait.&lt;/p&gt;    &lt;p&gt;Les applications web sont déployées dans &lt;code&gt;/usr/local/www&lt;/code&gt; sous FreeBSD, le contenu des sites se trouve dans &lt;code&gt;/usr/local/www/data&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;
# cd /usr/local/www
# tar zxvf ~/src/dotclear-2.0-beta7.tar.gz
# mv dotclear dotclear2
&lt;/pre&gt;

&lt;p&gt;Le renommage du répertoire permet d'éviter un conflit dans l'éventualité de l'installation d'un dotclear 1.&lt;/p&gt;


&lt;p&gt;Une fois le blog installé, il faut le &lt;a href=&quot;http://doc.dotclear.net/2.0/admin/install&quot;&gt;configurer&lt;/a&gt;. La configuration de lighty est très simple, il suffit d'ajouter l'alias suivant pour un blog qui est atteint par http://www.bon.org/blog par exemple&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
alias.url += (
        &amp;quot;/blog&amp;quot; =&amp;gt; &amp;quot;/usr/local/www/dotclear2&amp;quot;
)
&lt;/pre&gt;

&lt;p&gt;La suite de la configuration a déjà été abordée dans un autre &lt;a href=&quot;http://www.bonz.org/tech/post/2007/11/07/Et-si-je-racontais-ma-vie-avec-lighttpd&quot;&gt;billet&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;Finalement la modification est assez mineure et revient uniquement à modifier le chemin d'alias pour tous les blogs hormis celui par défaut et ajouter un alias pour celui-ci.&lt;/p&gt;</description>
    
    
    
          <comments>http://www.bonz.org/tech/post/2008/02/14/Un-blog-bien-sous-tout-rapport#comment-form</comments>
      <wfw:comment>http://www.bonz.org/tech/post/2008/02/14/Un-blog-bien-sous-tout-rapport#comment-form</wfw:comment>
      <wfw:commentRss>http://www.bonz.org/tech/feed/rss2/comments/117</wfw:commentRss>
      </item>
    
  <item>
    <title>ALETER Out of memory</title>
    <link>http://www.bonz.org/tech/post/2008/02/13/ALETER-Out-of-memory</link>
    <guid isPermaLink="false">urn:md5:9804e411ea169baee893505ddeed88e0</guid>
    <pubDate>Wed, 13 Feb 2008 18:29:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>système</category>
        <category>dotclear 2</category><category>freebsd</category><category>mysql5</category>    
    <description>&lt;p&gt;Si comme &lt;a href=&quot;http://fr.wikipedia.org/wiki/Le_Monde_de_Nemo&quot;&gt;Doris&lt;/a&gt; vous avez de petits problèmes de mémoire avec &lt;a href=&quot;http://dev.dotclear.net/2.0/&quot;&gt;dotclear 2&lt;/a&gt; (dc2 beta7) lors du chargement de la page d'administration des utilisateurs, le problème vient probablement de MySQL.&lt;/p&gt;    &lt;p&gt;Je me retrouve actuellement devant l'erreur suivante lorsque j'essaie d'administrer mes utilisateurs via l'interface d'administration&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;Out of memory; restart server and try again (needed 65528 bytes) (1037)
&lt;/pre&gt;

&lt;p&gt;&lt;img src=&quot;http://www.bonz.org/tech/public/tech/out_of_memory_dc2.png&quot; alt=&quot;Out of memory&quot; style=&quot;display:block; margin:0 auto;&quot; /&gt;
Le problème me semble venir du serveur MySQL, une petite recherche &lt;a href=&quot;http://www.google.com/search?q=Out+of+memory%3B+restart+server+and+try+again&amp;amp;ie=utf-8&amp;amp;oe=utf-8&amp;amp;aq=t&amp;amp;rls=com.ubuntu:en-US:official&amp;amp;client=firefox-a&quot;&gt;google&lt;/a&gt; me conforte dans cette idée, je mets donc un peu de code de debug dans &lt;code&gt;admin/users.php&lt;/code&gt; et je me retrouve avec cette requête SQL dantesque  générée par la fonction &lt;code&gt;getUsers()&lt;/code&gt;&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;SELECT U.user_id,user_super,user_status,user_pwd,user_name,user_firstname,user_displayname,user_email,user_url,user_desc, user_lang,user_tz, user_post_status,user_options, count(P.post_id) AS nb_post FROM dc_user U LEFT JOIN dc_post P ON U.user_id = P.user_id WHERE NULL IS NULL GROUP BY U.user_id,user_super,user_status,user_pwd,user_name,user_firstname,user_displayname,user_email,user_url,user_desc, user_lang,user_tz,user_post_status,user_options ORDER BY U.user_id ASC  LIMIT 30 OFFSET 0;
&lt;/pre&gt;

&lt;p&gt;Je comprends bien pourquoi mon petit serveur râle, devant l'adversité je me plie et je reconfigure mon serveur dans &lt;code&gt;/var/db/mysql/my.cnf&lt;/code&gt; en modifiant une seule ligne&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;sort_buffer_size = 128K
&lt;/pre&gt;

&lt;p&gt;Après un redémarrage du serveur MySQL, tout rentre dans l'ordre et everybody is content.
&lt;img src=&quot;http://www.bonz.org/tech/public/tech/users_dc2.png&quot; alt=&quot;Utilisateurs&quot; style=&quot;display:block; margin:0 auto;&quot; /&gt;&lt;/p&gt;</description>
    
    
    
          <comments>http://www.bonz.org/tech/post/2008/02/13/ALETER-Out-of-memory#comment-form</comments>
      <wfw:comment>http://www.bonz.org/tech/post/2008/02/13/ALETER-Out-of-memory#comment-form</wfw:comment>
      <wfw:commentRss>http://www.bonz.org/tech/feed/rss2/comments/116</wfw:commentRss>
      </item>
    
  <item>
    <title>HAL&gt; Affirmative, Dave, I read you.</title>
    <link>http://www.bonz.org/tech/post/2008/01/10/HAL-Affirmative-Dave-I-read-you</link>
    <guid isPermaLink="false">urn:md5:1b24cb50e2d7f4d973321a63fd0da31d</guid>
    <pubDate>Fri, 11 Jan 2008 17:39:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>freebsd</category><category>irc</category><category>linux</category><category>python</category>    
    <description>&lt;p&gt;&lt;img src=&quot;http://www.bonz.org/tech/public/tech/Hal-9000.jpg&quot; alt=&quot;HAL&quot; style=&quot;float:right; margin: 0 0 1em 1em;&quot; /&gt; Ah la vacuité d'une session &lt;a href=&quot;http://www.faqs.org/rfcs/rfc1459.html&quot; hreflang=&quot;en&quot;&gt;IRC&lt;/a&gt; sur un canal dirigé par un automate. Quoi de plus extraordinaire que pouvoir connaitre la météo de &lt;a href=&quot;http://www.mairie-felletin.fr/&quot; hreflang=&quot;fr&quot;&gt;Felletin dans la Creuse&lt;/a&gt; ou d'avoir une &lt;a href=&quot;http://eggnet.warpzone.org/moduledesc.php?module=9&quot; hreflang=&quot;en&quot;&gt;blague carambar&lt;/a&gt; à la demande. En ce moment je fais beaucoup de python, donc je me suis penché sur le problème pour un &lt;a href=&quot;http://www.binaries.fr/blog/&quot; hreflang=&quot;fr&quot;&gt;ami&lt;/a&gt;. Je vais essayer de vous exposer les clés de l'utilisation d'&lt;code&gt;&lt;a href=&quot;http://python-irclib.sourceforge.net/&quot; hreflang=&quot;en&quot;&gt;irclib&lt;/a&gt;&lt;/code&gt; pour construire un automate irc simple.&lt;/p&gt;    &lt;p&gt;L'automate est présenté ci-après, ses fonctions sont simplement la connexion à un serveur irc local sur le port 6667 et l'entrée dans le canal #test. L'automate s'attend à lire des commandes préfixées de '&lt;code&gt;!&lt;/code&gt;' dans le canal. La commande &lt;code&gt;!cycle&lt;/code&gt; demande à l'automate de partir et revenir sur le canal, la commande &lt;code&gt;!die&lt;/code&gt; provoque la déconnexion du serveur puis l'arrêt de l'automate. Le script de l'automate ci-après se trouve en téléchargement &lt;a href=&quot;http://www.bonz.org/files/mybot.py&quot;&gt;ici&lt;/a&gt; et peut être lancé par la commande &lt;code&gt;python mybot.py&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;
import sys
try:
    from ircbot import SingleServerIRCBot
    import irclib
except ImportError, e:
    print e
    sys.exit(1)

class pybot(SingleServerIRCBot):
    def __init__(self, server, nick, realname, chan):
        SingleServerIRCBot.__init__(self, server, nick, realname)
        self.chan = chan
        self.nick = nick

    ## Event callbacks
    def on_welcome(self, c, e):
        if self.chan:
            c.join(self.chan)

    def on_join(self, c, e):
        who = e.source().split('!', 1)
        if who[0] == self.nick:
            c.privmsg(e.target(), 'Hello world!')

    def on_pubmsg(self, c, e):
        msg = e.arguments()[0].strip()
        if msg.startswith('!'):
            cmd = msg[1:].split(' ', 1)[0]
            self.trigger(c, e, cmd)

    ## Command triggers
    def trigger(self, c, e, cmd):
        try:
            getattr(self, 'cmd_%s' % cmd)(c, e)
        except AttributeError:
            pass

    def cmd_cycle(self, c, e):
        chan = e.target()
        c.part(chan, 'cycle')
        c.join(chan)

    def cmd_die(self, c, e):
        SingleServerIRCBot.die(self, 'Argh')

## Here comes the real script
if __name__ == '__main__':
    server = [('localhost', 6667)]
    nick = 'monbot'
    realname = 'monbot'
    channel = '#test'

    bot = pybot(server, nick, realname, channel)
    try:
        bot.start()
    except KeyboardInterrupt:
        bot.connection.disconnect(&amp;quot;Ctrl-C m'a tuer&amp;quot;)
        bot.connection.close()
&lt;/pre&gt;


&lt;p&gt;La librairie &lt;code&gt;irclib&lt;/code&gt; est basée sur la gestion d'évènements, des méthodes vont être lancées sur apparition d'un évènement au sens IRC. Les méthodes de l'objet &lt;code&gt;pybot&lt;/code&gt; commençant par '&lt;code&gt;on_&lt;/code&gt;' sont les méthodes de gestion d'évènements, par exemple lorsqu'un message est envoyé sur un canal (&lt;code&gt;PUBMSG&lt;/code&gt;) la méthode on_pubsmg() est appelée. La création de l'automate se base sur la surcharge des méthodes de gestion d'évènements. Ces méthodes ont toutes la même définition d'appel&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
def on_foo(self, c, e):
    [...]
&lt;/pre&gt;

&lt;p&gt;La variable &lt;code&gt;c&lt;/code&gt; est un objet &lt;code&gt;ServerConnection&lt;/code&gt;, cet objet permet de manipuler tout ce qui concerne le serveur irc d'où provient l'évènement. On peut par exemple rejoindre un canal (&lt;code&gt;c.join()&lt;/code&gt;) ou envoyer un message à un canal ou une personne (commande &lt;code&gt;PRIVMSG&lt;/code&gt; via &lt;code&gt;c.privmsg()&lt;/code&gt;). La variable &lt;code&gt;e&lt;/code&gt; est un objet &lt;code&gt;Event&lt;/code&gt;, elle permet d'obtenir toutes les informations sur l'évènement qui a provoqué l'appel de la méthode. L'origine de l'évènement (&lt;code&gt;e.source()&lt;/code&gt;), la destination de cet évènement (&lt;code&gt;e.target()&lt;/code&gt;) ou encore les arguments de cet évènement (&lt;code&gt;e.arguments()&lt;/code&gt;). Toutes les informations sur ces 2 méthodes se trouvent dans le module irclib (&lt;code&gt;pydoc irclib&lt;/code&gt;).&lt;/p&gt;


&lt;p&gt;A partir de la connaissance de ces éléments de base, la construction d'un automate devient assez simple. Si l'automate doit effectuer des tâches régulières, les méthodes &lt;code&gt;execute_at()&lt;/code&gt; et &lt;code&gt;execute_delayed()&lt;/code&gt; (héritage de l'objet IRC parent de SingleServerIRCBot) peuvent être utilisées. Plus d'information sur &lt;code&gt;irclib&lt;/code&gt; devrait se trouver sur mon &lt;a href=&quot;http://wiki.bonz.org/python/irclib&quot; hreflang=&quot;fr&quot;&gt;wiki&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;Il est également possible d'utiliser &lt;a href=&quot;http://twistedmatrix.com/documents/current/api/twisted.words.protocols.irc.html&quot; hreflang=&quot;en&quot;&gt;twisted.words&lt;/a&gt; pour effectuer la même chose mais ça me semble une librairie un peu imposante pour un simple automate.&lt;/p&gt;


&lt;p&gt;Il faut noter qu'on peut faire également le même genre de chose en &lt;a href=&quot;http://search.cpan.org/~bingos/POE-Component-IRC-5.48/lib/POE/Component/IRC.pm&quot; hreflang=&quot;en&quot;&gt;Perl&lt;/a&gt;.&lt;/p&gt;</description>
    
    
    
          <comments>http://www.bonz.org/tech/post/2008/01/10/HAL-Affirmative-Dave-I-read-you#comment-form</comments>
      <wfw:comment>http://www.bonz.org/tech/post/2008/01/10/HAL-Affirmative-Dave-I-read-you#comment-form</wfw:comment>
      <wfw:commentRss>http://www.bonz.org/tech/feed/rss2/comments/115</wfw:commentRss>
      </item>
    
  <item>
    <title>WOPR&gt; Global Thermonuclear War</title>
    <link>http://www.bonz.org/tech/post/2008/01/03/WOPR-Global-Thermonuclear-War</link>
    <guid isPermaLink="false">urn:md5:e11fe7ac496ab81a907768d677602a9c</guid>
    <pubDate>Thu, 03 Jan 2008 18:33:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>freebsd</category><category>linux</category><category>python</category>    
    <description>&lt;p&gt;Dans mon parcours d'initiation à python, je me suis retrouvé face à un problème existentiel (ce qui donne tout le caractère initiatique au parcours, le problème, parce qu'il est existentiel ... laisse tomber). Lors de l'utilisation du module &lt;a href=&quot;http://docs.python.org/lib/module-cmd.html&quot; hreflang=&quot;en&quot;&gt;cmd&lt;/a&gt;, comment gérer correctement les interruptions classiques que sont &lt;code&gt;ctrl+D&lt;/code&gt; (&lt;code&gt;EOF&lt;/code&gt;) et &lt;code&gt;ctrl-C&lt;/code&gt; (&lt;code&gt;SIGINT&lt;/code&gt;).&lt;/p&gt;    &lt;p&gt;Ci-après, un squelette pour la gestion des 2 signaux évoqués. Juste après ci-après, les quelques explications qui s'imposent.&lt;/p&gt;
&lt;pre&gt;
import sys
import cmd

class shell(cmd.Cmd):
    def __init__(self):
        cmd.Cmd.__init__(self)

    # Let's handle ctrl+D signals and quit gently
    def do_EOF(self, str):
        # CLEAN HERE
        return 1

if __name__ == &amp;quot;__main__&amp;quot;:
    console = shell()
    try:
        console.cmdloop()
    except KeyboardInterrupt:
        # CLEAN HERE
        sys.exit(0)
&lt;/pre&gt;


&lt;h2&gt;&lt;code&gt;ctrl-D&lt;/code&gt; ou la fin du monde&lt;/h2&gt;

&lt;p&gt;Le manuel de &lt;a href=&quot;http://docs.python.org/lib/Cmd-objects.html#l2h-4880&quot; hreflang=&quot;en&quot;&gt;cmd&lt;/a&gt; nous dit&amp;nbsp;:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;An end-of-file on input is passed back as the string 'EOF'.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Ce qui signifie que la combinaison de touches &lt;code&gt;ctrl+D&lt;/code&gt; revient à gérer la commande &lt;code&gt;EOF&lt;/code&gt;, la méthode appelée est donc &lt;code&gt;do_EOF()&lt;/code&gt;.&lt;br /&gt;
Une autre information à propos de &lt;code&gt;cmdloop()&lt;/code&gt; donnée dans le manuel est&amp;nbsp;:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;This method will return when the postcmd() method returns a true value. The stop argument to postcmd() is the return value from the command's corresponding do_*() method.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Donc si la méthode &lt;code&gt;do_EOF()&lt;/code&gt; retourne simplement 1, la boucle va tout bêtement s'arrêter.&lt;/p&gt;


&lt;h2&gt;&lt;code&gt;ctrl-C&lt;/code&gt; ou la fin de l'univers&lt;/h2&gt;

&lt;p&gt;En Python, l'utilisation de &lt;code&gt;ctrl-C&lt;/code&gt; lève l'exception &lt;code&gt;&lt;a href=&quot;http://docs.python.org/lib/module-exceptions.html#l2h-111&quot; hreflang=&quot;en&quot;&gt;KeyboardInterrupt&lt;/a&gt;&lt;/code&gt;, en capturant cette interruption, on permet au script de sortir de façon maitrisée.&lt;/p&gt;


&lt;h2&gt;Evolution&lt;/h2&gt;

&lt;p&gt;Il est conseillé d'ajouter des actions de nettoyage au niveau des commentaires &lt;code&gt;# CLEAN HERE&lt;/code&gt; pour permettre une sortie de la console vraiment sans encombre. Il reste encore à trouver un moyen élégant d'utiliser &lt;code&gt;ctrl-c&lt;/code&gt; pour annuler la ligne en cours et récupérer l'invite vierge sans quitter &lt;code&gt;cmd.cmdloop()&lt;/code&gt;&lt;br /&gt;
Attention toutefois, ce code n'a pas été testé dans un environnement microsoft. Les signaux ne sont surement pas traités de la même façon, ça n'étonnera personne.&lt;/p&gt;</description>
    
    
    
      </item>
    
  <item>
    <title>Colle toi ça dans les oreilles</title>
    <link>http://www.bonz.org/tech/post/2007/11/30/Colle-toi-ca-dans-les-oreilles</link>
    <guid isPermaLink="false">urn:md5:c1280926d2be28bc06efb7eba79ad1d4</guid>
    <pubDate>Fri, 30 Nov 2007 17:21:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>hardware</category>
        <category>7.10</category><category>gutsy</category><category>iaudio</category><category>linux</category><category>ubuntu</category><category>x5l</category>    
    <description>    &lt;p&gt;Je suis l'heureux possesseur d'un &lt;a href=&quot;http://www.cowonglobal.com/product/product_X5_feature.php&quot;&gt;iAUDIO X5L de 30Go&lt;/a&gt;, cet appareil est vraiment génial. Sauf quand il vous claque des montages en lecture seule, là c'est un peu pénible.&lt;/p&gt;
&lt;pre&gt;
Nov 30 16:36:09 ewok kernel: [23799.560000] FAT: Filesystem panic (dev sdb1)
Nov 30 16:36:09 ewok kernel: [23799.560000]     fat_get_cluster: invalid cluster chain (i_pos 0)
Nov 30 16:36:09 ewok kernel: [23799.560000]     File system has been set read-only
&lt;/pre&gt;

&lt;p&gt;Pas de problème, il suffit de faire une petite vérification du système de fichier après avoir démonté la partition&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
$ sudo umount /media/IAUDIO
$ sudo fsck.vfat -a -v /dev/sdb1
dosfsck 2.11 (12 Mar 2005)
dosfsck 2.11, 12 Mar 2005, FAT32, LFN
Checking we can access the last sector of the filesystem
Boot sector contents:
System ID &amp;quot;MSDOS5.0&amp;quot;
Media byte 0xf8 (hard disk)
[...]
Reclaiming unconnected clusters.
Reclaimed 18671 unused clusters (305905664 bytes) in 73 chains.
Checking free cluster summary.
Free cluster summary wrong (98499 vs. really 98722)
  Auto-correcting.
Performing changes.
/dev/sdb1: 6625 files, 1731289/1830011 clusters
&lt;/pre&gt;

&lt;p&gt;La reconnexion du lecteur à mon port USB et des mouvements de fichiers sur le disque me confirment que le problème a disparu. YATAAAAAAAAAAAA.&lt;/p&gt;</description>
    
    
    
      </item>
    
  <item>
    <title>Rooooooooxanne</title>
    <link>http://www.bonz.org/tech/post/2007/11/29/Rooooooooxanne</link>
    <guid isPermaLink="false">urn:md5:cfd1f01a515fd6d23ce16069008c5dba</guid>
    <pubDate>Thu, 29 Nov 2007 13:04:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>système</category>
        <category>debian</category><category>font</category><category>truetype</category><category>ubuntu</category>    
    <description>&lt;p&gt;J'ai eu besoin d'installer une jolie police de caractère (font en anglois) sur une ubuntu/debian (ce sont les seules distributions que j'ai testé) récemment et je me suis rendu compte de deux choses&amp;nbsp;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;C'est très simple à faire&lt;/li&gt;
&lt;li&gt;C'est peu documenté&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Donc voici la marche à suivre pour installer une police de caractère sans les droits administrateur.&lt;/p&gt;    &lt;p&gt;Il faut, &lt;em&gt;a priori&lt;/em&gt;, trouver un &lt;a href=&quot;http://del.icio.us/sberder/font&quot;&gt;belle police à installer&lt;/a&gt; (à noter les &quot;Free Fonts Of The Month&quot; de SmashingMagazine). Après avoir téléchargé la police en question et décompressé éventuellement une archive, les fichiers &lt;code&gt;.ttf&lt;/code&gt; sont disponible.&lt;/p&gt;
&lt;pre&gt;
$ wget http://www.kottke.org/plus/type/silkscreen/download/silkscreen.tar.gz -P ~/tmp
[...]
$ tar zxvf ~/tmp/silkscreen.tar.gz -C ~/tmp/
slkscr.ttf
slkscrb.ttf
slkscre.ttf
slkscreb.ttf
readme.txt
&lt;/pre&gt;

&lt;p&gt;Un fois les fichiers de police à disposition, il suffit de créer un répertoire &lt;code&gt;.fonts&lt;/code&gt; dans son répertoire &lt;code&gt;home&lt;/code&gt;, d'y placer les polices et rafraichir le cache de polices.&lt;/p&gt;
&lt;pre&gt;
$ mkdir ~/.fonts
$ cp ~/tmp/*.ttf ~/.fonts
$ fc-cache -f -v ~/.fonts/
/home/hr/.fonts/: caching, 1 fonts, 0 dirs
/var/cache/fontconfig: not cleaning unwritable cache directory
/home/hr/.fontconfig: cleaning cache directory
fc-cache: succeeded
&lt;/pre&gt;

&lt;p&gt;Et c'est parti, pas plus compliqué que ça.&lt;/p&gt;</description>
    
    
    
      </item>
    
</channel>
</rss>