tag:linuxfr.org,2005:/users/tchetchLinuxFr.org : les contenus de Etienne Bagnoud2015-05-04T22:47:30+02:00/favicon.pngtag:linuxfr.org,2005:Diary/358092015-04-24T14:33:30+02:002015-04-24T14:33:30+02:00Retour vers le futur !Licence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<p>L'autre jour, je découvrais, via la dépêche sur <a href="//linuxfr.org/news/un-point-d-avancement-sur-neovim">neovim</a>, un format « comme JSON mais rapide et petit » : <a href="http://msgpack.org/">MessagePack</a>.<br>
<br><a href="http://fr.wikipedia.org/wiki/S%C3%A9rialisation">Sérialiser</a> des données est le fait de coder des données depuis un format applicatif interne à un format utilisé pour les communiquer ou les stocker. De fait, on pourrait préférer sérialiser les données dans un format générique afin de les partager avec un maximum d'applications.<br>
Une méthode qui marche bien pour sérialiser, c'est des séquences TLV, soit Type-Length-Value (ou Tag-Length-Value, c'est selon). L'idée est de mettre un type (ou tag), permettant d'indiquer quelle donnée nous aurons, ensuite sa longueur, afin d'indiquer l'espace nécessaire, puis la donnée.<br>
Le premier avantage, évident, est la possibilité de sélectionner la méthode de traitement ainsi que réserver l'espace nécessaire avant de faire face à la donnée.<br>
Le deuxième avantage est qu'il suffit de documenter, ou standardiser, les types de données afin d'obtenir un format d'échange suffisamment générique pour avoir des applications capables de représenter tous les types de données sans forcément savoir les interpréter. <br>
Le troisième avantage est qu'on peut utiliser n'importe quelle représentation :<br>
</p>
<pre><code># Une version pseudo-binaire :
# TAG LENGTH VALUE
+--------+--------+---------+
| 0xA0 | 0x05 | HELLO |
+--------+--------+---------+
# Une version CSV :
STRING,5,HELLO
# Une version XML :
<string length="5">HELLO</string>
# Une version JSON :
{ string : { length: 5, value : "HELLO" } }
</code></pre>
<p> <br>
Avec MessagePack, nous avons une représentation binaire.<br>
<br>
Là, mon application peut transmettre des messages entre ses différentes instances. Mais uniquement entre ses instances ; savoir qu'on a une chaîne de 5 caractères « HELLO » est inutile si on ne sait pas l'interpréter.<br>
L'autre information qu'on trouve sur le site de MessagePack, c'est cette image :</p>
<p><img src="//img.linuxfr.org/img/687474703a2f2f6d73677061636b2e6f72672f696d616765732f696e74726f2e706e67/intro.png" alt='{ "compact": true, "schema": 0}' title="Source : http://msgpack.org/images/intro.png"></p>
<p>Je comprends cette image comme « Ce qui est cool avec MessagePack, c'est l'absence de schémas » (je lis « "schema": 0 »).<br>
</p>
<pre><code class="json"><span class="p">{</span> <span class="nt">"compact"</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nt">"schema"</span><span class="p">:</span> <span class="mi">0</span><span class="p">}</span></code></pre>
<p> <br>
Ce qui est faux et va être encore plus faux si je crois la section « Future discussion » de la spécification.<br>
<br>
Faux car s'il n'y a pas de schéma explicite (sous-entendre « <strong>documenté</strong> »), le développeur va devoir coder et décoder un message selon un même <strong>schéma</strong>. Idem si deux applications doivent communiquer.<br>
Encore plus faux car il semble que les développeurs de MessagePack discutent de la possibilité d'introduire des schémas (ou quelque chose de similaire).<br>
<br>
En prenant ma machine à voyager dans le temps, j'ai fais un bond dans le passé pour déterrer le future de MessagePack : <strong><a href="http://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One">ASN.1</a></strong>.<br>
<br>
D'accord, lire des normes (surtout ISO) c'est plus chiant que de les réinventer. Mais ASN.1 pour le schéma et un encodage BER (Basic Encoding Rules), on arrive à MessagePack avec schémas … et avec moins de limitations (oui avec BER on peut sortir du pur TLV si nos données sont trop longues), avec des cas déjà résolus (deux implémentations compatibles MessagePack pourraient très bien se comprendre sans donner exactement le même résultat à l'encodage ou au décodage. Dès que vous voulez signer vos données, c'est problématique … ASN.1 -> DER), avec des types de données franchement cool, possibilité d'étendre à l'infini (ou presque) et par application, …<br>
<br>
Et si vous voulez juste sérialiser vos données sans schéma, faites <code>man ber_printf</code> et <code>man ber_scanf</code>. Vous êtes sur une système de type Microsoft Windows ? C'est aussi <a href="https://msdn.microsoft.com/en-us/library/aa366089(v=vs.85).aspx">présent</a>. Universellement disponible depuis longtemps.<br>
<br>
Ce n'est pas le premier projet qui me fais me dire qu'il manque des cours d'histoire et culture générale de l'informatique dans les formations. Et je n'ai pas l'impression qu'historien spécialisé en informatique existe …<br>
<br>
PS: Il n'y a rien de « comme JSON » dans MessagePack, c'est juste un schéma faisable avec MessagePack et BER … Ensuite :</p>
<p> <br><strong>BER</strong><br>
</p>
<pre><code class="c"><span class="n">BerElement</span> <span class="o">*</span> <span class="n">Message</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="n">Message</span> <span class="o">=</span> <span class="kt">ber_alloc_t</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span> <span class="cm">/* ou LBER_USE_DER si on veut utiliser DER */</span>
<span class="k">if</span><span class="p">(</span><span class="n">Message</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
<span class="n">ber_printf</span><span class="p">(</span><span class="n">Message</span><span class="p">,</span> <span class="s">"{oboi}"</span><span class="p">,</span> <span class="s">"compact"</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="s">"schema"</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="p">}</span></code></pre>
<p> <br><strong>MessagePack</strong> (adapté d'un exemple de la documentation)<br>
</p>
<pre><code class="c"><span class="n">msgpack_sbuffer</span> <span class="n">sbuf</span><span class="p">;</span>
<span class="n">msgpack_packer</span> <span class="n">pk</span><span class="p">;</span>
<span class="cm">/* msgpack::sbuffer is a simple buffer implementation. */</span>
<span class="n">msgpack_sbuffer_init</span><span class="p">(</span><span class="o">&</span><span class="n">sbuf</span><span class="p">);</span>
<span class="cm">/* serialize values into the buffer using msgpack_sbuffer_write callback function. */</span>
<span class="n">msgpack_packer_init</span><span class="p">(</span><span class="o">&</span><span class="n">pk</span><span class="p">,</span> <span class="o">&</span><span class="n">sbuf</span><span class="p">,</span> <span class="n">msgpack_sbuffer_write</span><span class="p">);</span>
<span class="n">msgpack_pack_map</span><span class="p">(</span><span class="o">&</span><span class="n">pk</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
<span class="n">msgpack_pack_str</span><span class="p">(</span><span class="o">&</span><span class="n">pk</span><span class="p">,</span> <span class="mi">7</span><span class="p">);</span>
<span class="n">msgpack_pack_str_body</span><span class="p">(</span><span class="o">&</span><span class="n">pk</span><span class="p">,</span> <span class="s">"compact"</span><span class="p">,</span> <span class="mi">7</span><span class="p">);</span>
<span class="n">msgpack_pack_true</span><span class="p">(</span><span class="o">&</span><span class="n">pk</span><span class="p">);</span>
<span class="n">msgpack_pack_str</span><span class="p">(</span><span class="o">&</span><span class="n">pk</span><span class="p">,</span> <span class="mi">6</span><span class="p">);</span>
<span class="n">msgpack_pack_str_body</span><span class="p">(</span><span class="o">&</span><span class="n">pk</span><span class="p">,</span> <span class="s">"schema"</span><span class="p">,</span> <span class="mi">6</span><span class="p">);</span>
<span class="n">msgpack_pack_int</span><span class="p">(</span><span class="o">&</span><span class="n">pk</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span></code></pre><div><a href="https://linuxfr.org/users/tchetch/journaux/retour-vers-le-futur.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/105547/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/tchetch/journaux/retour-vers-le-futur#comments">ouvrir dans le navigateur</a>
</p>
Etienne Bagnoudhttps://linuxfr.org/nodes/105547/comments.atomtag:linuxfr.org,2005:Diary/340232013-06-19T16:17:03+02:002013-06-19T16:17:03+02:00Modification d'un paquet DebianLicence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<h2 id="sommaire">Sommaire</h2>
<ul><li>
<a href="#toc_0">Les outils de base</a>
</li>
<li>
<a href="#toc_1">Les sources</a>
</li>
<li>
<a href="#toc_2">Nettoyage</a>
</li>
<li>
<a href="#toc_3">Patcher</a>
</li>
<li>
<a href="#toc_4">Compilation</a>
</li>
<li>
<a href="#toc_5">Conclusion</a>
</li>
<li>
<a href="#toc_6">PS</a>
</li>
</ul><p>Il y a fort longtemps, j'ai modifié mon premier paquet Debian. Puis j'ai eu à le refaire. Puis encore une fois. Mais à chaque fois je notais rien de ma démarche. À chaque fois je recommençais presque de zéro. J'ai donc décidé de m'arrêter un instant pour documenter. Certes ça a été documenté et re-documenté des centaines de fois sur le Web, mais je le fais pour moi et si je le publie ici c'est pour m'assurer de ne pas perdre cette documentation (et parce que Facebook ne supporte pas les statuts avec la syntaxe Markdown). Et, finalement, si je le publie rien que pour moi ici, ça pourrait être utile à quelqu'un.</p>
<h3 id="toc_0">Les outils de base</h3>
<p>Pour commencer nous allons installer les outils de base. On retrouve la liste sur le <a href="http://wiki.debian.org/AdvancedBuildingTips">wiki Debian</a> sauf que depuis la sortie de Wheezy, <code>diff</code> doit être remplacé par <code>diffutils</code>. Je rajoute aussi un outil : <a href="http://wiki.debian.org/UsingQuilt">quilt</a>, un outil de gestion de patch utilisé dans les paquets Debian. Cet outil, <a href="http://en.wikipedia.org/wiki/Quilt_%28software%29">dixit Wikipedia</a>, a été créé pour gérer les patchs du noyau Linux et est maintenant intégré à la gestion de paquet Debian.</p>
<pre>
<code class="sh"><span class="c"># aptitude install build-essential devscripts lintian diffutils patch patchutils quilt</span>
</code>
</pre>
<p>Une fois ces paquets installé je configure brièvement <code>quilt</code> pour un usage debianesque en ajoutant dans <code>~/.quiltrc</code> les lignes suivantes (d'autres méthodes sont proposées sur le <a href="http://wiki.debian.org/UsingQuilt">wiki</a>) :</p>
<pre>
<code class=""> QUILT_PATCHES=debian/patches
QUILT_NO_DIFF_INDEX=1
QUILT_NO_DIFF_TIMESTAMPS=1
QUILT_REFRESH_ARGS="-p ab"
</code>
</pre><h3 id="toc_1">Les sources</h3>
<p>Le logiciel que je souhaite modifier porte le doux nom de <code>dlm-pcmk</code> et trouve sa source dans <code>redhat-cluster</code>. Afin d'obtenir tout ça, je me place dans mon répertoire de travail, par exemple <code>~/src/redhat-cluster</code>. Tout étant prêt, je lance deux sortilèges :</p>
<pre>
<code class="sh"> <span class="nv">$ </span>apt-get <span class="nb">source </span>redhat-cluster
<span class="c"># aptitude build-dep redhat-cluster</span>
</code>
</pre>
<p>Le premier me retourne les sources prêtes pour être modifiées (décompressées et tout, si si), le deuxième m'obtient tout ce qui sera nécessaire pour les compiler. Les sources sont dans un répertoire nommé <code>redhat-cluster-3.0.12</code> ; c'est là que tout va se dérouler ensuite (sous-entendre <code>$ cd redhat-cluster-3.0.12</code>).</p>
<h3 id="toc_2">Nettoyage</h3>
<p>Afin de m'assurer d'avoir un environnement propre pour travailler, je demande aux petits lutins de le faire pour moi en utilisant leur langue et en me faisant passer pour la racine magique.</p>
<pre>
<code class="sh"> <span class="nv">$ </span>fakeroot debian/rules clean
</code>
</pre><h3 id="toc_3">Patcher</h3>
<p>À ce stade, il est temps de patcher les sources. Comme par hasard (si si), ce paquet source utilise <code>quilt</code> pour la gestion des patches (ce n'est pas forcément le cas mais ça <a href="http://wiki.debian.org/Projects/DebSrc3.0">tend à le devenir</a>). Donc nous allons pousser tous les patches en stock sur les sources.</p>
<pre>
<code class="sh"> <span class="nv">$ </span>quilt push -a
</code>
</pre>
<p>Et nous allons créer notre nouveau patch.</p>
<pre>
<code class="sh"> <span class="nv">$ </span>quilt new fix-dev-write-no-op
</code>
</pre>
<p>Pour faire ce patch, je vais devoir modifier deux fichiers. Il faut donc les indiquer <strong>à priori</strong> (j'ai essayé de le faire à posteriori mais ça n'a pas fonctionner et je n'ai pas chercher à en savoir plus).</p>
<pre>
<code class="sh"> <span class="nv">$ </span>quilt add dlm/include/linux/dlm_plock.h
<span class="nv">$ </span>quilt add group/dlm_controld/plock.c
</code>
</pre>
<p>Je fais mes corrections dans ces deux fichiers et je ferme le tout très simplement (à noter que les commandes <code>quilt</code> doivent être faites à la racine du paquet source).</p>
<pre>
<code class="sh"> <span class="nv">$ </span>quilt refresh
<span class="nv">$ </span>quilt pop
</code>
</pre>
<p>Pour que mon changement ait de la gueule, je vais ajouter une entrée à la liste des changements Debian.</p>
<pre>
<code class="sh"> <span class="nv">$ </span>dch -i
</code>
</pre>
<p>Mon éditeur texte favori, <a href="http://office.microsoft.com/en-us/word/">Microsoft Word</a>, s'ouvre et je décris mon changement.</p>
<h3 id="toc_4">Compilation</h3>
<pre>
<code class="sh"> <span class="nv">$ </span>fakeroot debian/rules binary
</code>
</pre>
<p>Et voilà, dans mon répertoire <code>~/src/redhat-cluster</code> j'ai un ou plusieurs (dans mon cas plusieurs) <code>.deb</code> prêt a être installé sur mon système ou pousser dans mon dépôt Debian.</p>
<h3 id="toc_5">Conclusion</h3>
<p>Le bug corrigé ici a été <a href="http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=691749">signalé à Debian</a>, comme rien ne bougeait, j'ai adapté le patch de <a href="https://bugzilla.redhat.com/show_bug.cgi?id=731775">redhat</a>, j'ai soumis à Debian (avant la sortie de Wheezy), mais le bug est toujours présent … donc je suis condamné à patcher ce paquet encore et encore.<br />
Par ailleurs, si vous devez juste importer un patch avec <code>quilt</code>, après le <code>quilt push</code> il suffit de faire <code>quilt import $FILE</code>.</p>
<h3 id="toc_6">PS</h3>
<p>Si vous voulez nettoyer votre système à la fin (supprimer les build-dep), j'ai trouvé <a href="http://www.webupd8.org/2010/10/undo-apt-get-build-dep-remove-build.html">cette jolie ligne sur le web</a> :</p>
<pre>
<code class="sh"> <span class="nv">$ </span>sudo aptitude markauto <span class="k">$(</span>apt-cache showsrc redhat-cluster | grep Build-Depends | perl -p -e <span class="s1">'s/(?:[\[(].+?[\])]|Build-Depends:|,|\|)//g'</span><span class="k">)</span>
</code>
</pre>
<p>Ça marche du tonnerre (il faut remplacer <code>redhat-cluser</code> par le nom de votre paquet, bien entendu).</p><div><a href="https://linuxfr.org/users/tchetch/journaux/modification-d-un-paquet-debian.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/98754/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/tchetch/journaux/modification-d-un-paquet-debian#comments">ouvrir dans le navigateur</a>
</p>
Etienne Bagnoudhttps://linuxfr.org/nodes/98754/comments.atomtag:linuxfr.org,2005:Diary/334042012-11-22T15:13:29+01:002012-11-22T15:13:29+01:00Chantonnons en récursion Licence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<p>Dernièrement, je parlais, avec une connaissance (ça me change de parler tout seul), du très célèbre <a href="http://fr.wikipedia.org/wiki/Cat_Stevens">Yusuf Islam</a>. De cette conversation, l'envie de ré-écouter un peu de sa musique m'a prise à la gorge et je me suis décidé à acheter, légalement, sur <a href="http://thepiratebay.se">iTunes</a> (si ce lien a la couleur d'un lien déjà visité, ne bougez pas, la police arrive) , quelques un de ses grands tubes.</p>
<p>Au détour de la chanson "<a href="http://en.wikipedia.org/wiki/Catch_Bull_at_Four">Can't keep it in</a>", j'entends ce petit passage lyrique, qui semble sans conséquence mais qui est effectivement dramatique :</p>
<blockquote>
<p>Love is my blood<br />
Blood spin my head<br />
And my head fall in love</p>
</blockquote>
<p>En bon psychopathe de l'informatique que je suis, je n'ai pu me retenir de penser que la pile d'appel allait exploser !</p>
<p>Si on met ça en C, ça donnerait un code ressemblant à ça :</p>
<pre>
<code class="c"><span class="kt">void</span> <span class="n">love</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span>
<span class="kt">void</span> <span class="n">blood</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span>
<span class="kt">void</span> <span class="n">head</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span>
<span class="kt">void</span> <span class="nf">love</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
<span class="n">blood</span><span class="p">();</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">blood</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
<span class="n">head</span><span class="p">();</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">head</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
<span class="n">love</span><span class="p">();</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span> <span class="n">argv</span><span class="p">)</span> <span class="p">{</span>
<span class="n">love</span><span class="p">();</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code>
</pre>
<p>Depuis cette chanson, je pense tous les jours à la récursion. En fait, écouter cette chanson me fait penser à la récursion, penser à la récursion me fait penser à cette chanson, penser à cette chanson me fait écouter cette chanson … aaaargh :</p>
<pre>
<code class="c"><span class="kt">void</span> <span class="n">think_recurs</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span>
<span class="kt">void</span> <span class="n">think_cantkeepitin</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span>
<span class="kt">void</span> <span class="n">listen_cantkeepitin</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span>
<span class="kt">void</span> <span class="nf">think_recurs</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
<span class="n">think_cantkeepitin</span><span class="p">();</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">think_cantkeepitin</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
<span class="n">listen_cantkeepitin</span><span class="p">();</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">listen_cantkeepitin</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
<span class="n">think_recurs</span><span class="p">();</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span> <span class="n">argv</span><span class="p">)</span> <span class="p">{</span>
<span class="n">listen_cantkeepitin</span><span class="p">();</span> <span class="cm">/* c'est la ou tout a commence */</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="cm">/* jamais */</span>
<span class="p">}</span>
</code>
</pre>
<p>Voilà, voilà … Je vous laisse à vos occupations et je vais signer le registre d'entrée que me propose ce gentil monsieur en blouse blanche.</p>
<p>PS: Désolé, un des effets de bords de la chanson intitulée "Can't keep it in", soit "Peux pas le contenir", c'est qu'on ne peut pas le contenir et donc on en fait un journal (après avoir forcé toutes ses connaissances à écouter dix fois la chanson).</p><div><a href="https://linuxfr.org/users/tchetch/journaux/chantonnons-en-recursion.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/96518/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/tchetch/journaux/chantonnons-en-recursion#comments">ouvrir dans le navigateur</a>
</p>
Etienne Bagnoudhttps://linuxfr.org/nodes/96518/comments.atomtag:linuxfr.org,2005:Diary/330882012-09-09T11:32:13+02:002012-09-09T11:32:13+02:00Genèse d'un journalLicence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<p>Mon <a href="http://linuxfr.org/users/tchetch/journaux/realloc">journal précédent parlait de realloc</a> dont on ne contrôlait pas la valeur de retour. Suite à ce journal j'ai été très surpris par le nombre de commentaires clamant que ce n'était pas important, que le noyau se chargerait de tuer le processus, que le programme planterait, … Je vais donc expliquer ce qui m'a amener à écrire ce journal.</p>
<p>Je codais un petit truc vite fait en C et j'avais besoin d'utiliser <code>snprintf</code>. Cette fonction prend, en paramètre, la taille de la zone mémoire. Si cette zone mémoire ne suffit pas, elle retourne la taille qui aurait été nécessaire pour inscrire toute la chaîne de caractère. Il est difficile de savoir la taille à l'avance. Un simple <code>"%d"</code> peut nécessité de 1 à plus de X caractères. Il faut donc, dans certains cas, le faire en deux fois. On évalue la taille qui sera généralement suffisante et on prévoit le cas où ce n'est pas suffisant. Ça donne un code qui ressemble à ça :</p>
<pre>
<code class="c"><span class="cp">#define DEFAULT_SIZE 30</span>
<span class="kt">int</span> <span class="n">size</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span>
<span class="kt">char</span> <span class="o">*</span> <span class="n">str</span><span class="o">=</span><span class="nb">NULL</span><span class="p">;</span>
<span class="kt">char</span> <span class="o">*</span> <span class="n">tmp</span><span class="o">=</span><span class="nb">NULL</span><span class="p">;</span>
<span class="cm">/* ... */</span>
<span class="n">str</span><span class="o">=</span><span class="n">malloc</span><span class="p">(</span><span class="n">DEFAULT_SIZE</span> <span class="o">*</span> <span class="p">(</span><span class="k">sizeof</span> <span class="o">*</span> <span class="n">str</span><span class="p">));</span>
<span class="k">if</span><span class="p">(</span><span class="n">str</span><span class="o">!=</span><span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
<span class="n">size</span><span class="o">=</span><span class="n">snprintf</span><span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="n">DEFAULT_SIZE</span><span class="p">,</span> <span class="cm">/* ... */</span><span class="p">);</span>
<span class="cm">/* man page dit : "Thus, a return value of size or more means that the output </span>
<span class="cm"> * was truncated." */</span>
<span class="k">if</span><span class="p">(</span><span class="n">size</span><span class="o">>=</span><span class="n">DEFAULT_SIZE</span><span class="p">)</span> <span class="p">{</span>
<span class="cm">/* snprintf retourne taille sans le '\0' */</span>
<span class="n">size</span><span class="o">++</span><span class="p">;</span>
<span class="n">tmp</span><span class="o">=</span><span class="n">realloc</span><span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="n">size</span> <span class="o">*</span> <span class="p">(</span><span class="k">sizeof</span> <span class="o">*</span> <span class="n">str</span><span class="p">));</span>
<span class="k">if</span><span class="p">(</span><span class="n">tmp</span><span class="o">!=</span><span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
<span class="n">size</span><span class="o">=</span><span class="n">tmp</span><span class="p">;</span>
<span class="n">snprintf</span><span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="cm">/* ... */</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">free</span><span class="p">(</span><span class="n">str</span><span class="p">);</span>
<span class="n">str</span><span class="o">=</span><span class="nb">NULL</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code>
</pre>
<p>En écrivant la ligne <code>tmp=realloc</code>, j'avais commencé par écrire <code>str=realloc</code>. Je me suis arrêté net et j'ai pesté. J'ai pesté parce que je sais que c'est une erreur généralement acceptée. L'idée du journal m'était venue, il me manquait plus qu'un exemple concret dans un programme existant et utilisé.</p>
<p>Trouver un programme fut d'une simplicité enfantine. N'importe qu'elle programme qui semble manger beaucoup trop de mémoire est certainement bourré de ce genre d'erreur. J'avais remarqué conky il y'a bien longtemps. Avec un <code>VmPeak</code> de 281212 kB et un <code>VmData</code> de 82612 kB pour afficher, <em>en texte</em>, la date, la charge de la batterie, le nom du réseau Wifi et mon adresse IP, ça transpire le gaspillage.</p>
<p>Un <code>git clone</code> et un <code>grep -R realloc *</code> plus tard, j'avais une quinzaine d'exemple de <code>realloc</code> foireux. Soit tous les realloc, sans exception. Les malloc sont aussi foireux.</p>
<p>J'ai fait un patch pour ce que je pouvais, rapidement, corriger. <code>VmPeak</code>, sur la configuration par défaut, passe de 317916 kB à 211388 kB et <code>VmData</code> de 189372 kB à 123836 kB. Une amélioration notable après environ 4 ou 5 realloc corrigés.</p>
<p>Comme a commenté "pasBill pasGates", à propos de gérer correctement les retours de fonctions comme realloc ou malloc :</p>
<blockquote>
<p>[…] c'est une question d'hygiene de base de mon point de vue.</p>
</blockquote>
<p>Ne pas le faire, ce n'est pas seulement manquer d'hygiène, c'est surtout faire preuve de laxisme. Non, on ne peut pas compter sur le magicien "OOM Killer" de Linux. Le noyau a pu être compiler sans ça. Non, attendre un SEGFAULT pour quitter le programme n'est pas une solution. Et, surtout, ce n'est pas une optimisation que de gagner un ou deux cycles processeurs en enlevant un <code>if(x==NULL)</code>.</p>
<p>"pasBill pasGates" a aussi dit :</p>
<blockquote>
<p>Arreter le programme peut-etre oui, selon ce que le soft fait, mais il faut le faire proprement , pas avec un SEGFAULT ou un assert.</p>
</blockquote>
<p>C'est exactement le cas. Votre programme de traitement de texte n'a pas assez d'espace pour le prochain caractère donc il plante sans possibilité de sauvegarder ? Ce n'est pas une solution.<br />
Et l'excuse du programme non-critique n'en est pas une. Si un utilisateur décide d'utiliser votre programme, alors c'est un programme critique.</p>
<p>Le C est un langage qui impose cette gestion de mémoire. Si vous ne vous sentez pas de la faire, alors le C n'est pas pour vous. </p><div><a href="https://linuxfr.org/users/tchetch/journaux/genese-d-un-journal.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/95532/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/tchetch/journaux/genese-d-un-journal#comments">ouvrir dans le navigateur</a>
</p>
Etienne Bagnoudhttps://linuxfr.org/nodes/95532/comments.atomtag:linuxfr.org,2005:Diary/330822012-09-07T16:38:54+02:002012-09-07T16:38:54+02:00reallocLicence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<p>Sur mon netbook avec 1Go de RAM, je déteste particulièrement les programmes qui bouffent de la mémoire dans le vide. Et quand un <a href="http://conky.sourceforge.net/">programme qui affiche la date, le niveau de batterie et deux trois autres informations</a> mangent 4 fois plus que ce qu'utilise xmonad, je suppose un problème.</p>
<p>Je récupère le code source et je regarde. Du C qui est du C++ ou le contraire, mais bon, passons. Je n'ai pas prévu de faire la moindre correction au code. Je regarde juste … et je m'étrangle sur les deux lignes suivantes :</p>
<pre>
<code class="c"><span class="cm">/* ... */</span>
<span class="n">src_dup</span> <span class="o">=</span> <span class="p">(</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span> <span class="n">realloc</span><span class="p">(</span><span class="n">src_dup</span><span class="p">,</span> <span class="n">dup_len</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">char</span><span class="p">));</span>
<span class="n">sprintf</span><span class="p">(</span><span class="n">src_dup</span> <span class="o">+</span> <span class="n">dup_idx</span><span class="p">,</span> <span class="s">"%s"</span><span class="p">,</span> <span class="n">templates</span><span class="p">[</span><span class="n">tmpl_num</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]);</span>
<span class="cm">/* ... */</span>
</code>
</pre>
<p>Si on lit la page de man de realloc :</p>
<pre>
<code class="">void *realloc(void *ptr, size_t size);
[...]
The realloc() function returns a pointer to the newly allocated memory, which
is suitably aligned for any kind of variable and may be different from ptr,
or NULL if the request fails. If size was equal to 0, either NULL or a
pointer suitable to be passed to free() is returned. If realloc() fails the
original block is left untouched; it is not freed or moved.
</code>
</pre>
<p>Une utilisation correcte de realloc en C devrait ressembler à ça :</p>
<pre>
<code class="c"><span class="cm">/* ... */</span>
<span class="n">tmp</span><span class="o">=</span><span class="n">realloc</span><span class="p">(</span><span class="n">ptr</span><span class="p">,</span> <span class="n">new_size</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span><span class="n">tmp</span><span class="o">!=</span><span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
<span class="n">ptr</span><span class="o">=</span><span class="n">tmp</span><span class="p">;</span>
<span class="cm">/* ... */</span>
<span class="p">}</span>
</code>
</pre>
<p>Si la ré-allocation n'est pas possible, <code>NULL</code> est retourné. Ensuite le pointeur retourné peut être différent de celui passé en paramètre, il est donc nécessaire d'assigner la valeur retournée au bon pointeur.<br />
Vous me croyez pas ? La <a href="http://c-faq.com/malloc/realloc.html">FAQ C</a> ne laisse aucun doute.</p>
<p>Voilà, c'est tous ce que j'avais à dire. </p>
<p>Et puisqu'il semble qu'il ait une tradition qui fasse grincer des dents, voici quelque chose <a href="http://www.thelivingmoon.com/43ancients/04images/India/NuclearBomb.jpg">en rapport avec le journal</a>.</p>
<p>PS: Il faudrait que je patch les 15 realloc de conky, mais comme je sais pas si c'est du C ou du C++, je sais pas si je vais le faire.</p><div><a href="https://linuxfr.org/users/tchetch/journaux/realloc.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/95517/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/tchetch/journaux/realloc#comments">ouvrir dans le navigateur</a>
</p>
Etienne Bagnoudhttps://linuxfr.org/nodes/95517/comments.atomtag:linuxfr.org,2005:Diary/330492012-08-31T12:15:37+02:002012-08-31T12:15:37+02:00Netbook et niveau d'exécutionLicence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<p>Une petite astuce, évidente (mais les solutions évidentes sont celles dont on ne pense jamais), mais pratique pour gagner quelques petits dixième de watt ou un peu de réactivité sur votre netbook.</p>
<p>Si, comme moi, vous travaillez sur un netbook bas de gamme, vous êtes soucieux de chaque processus tournant en arrière-plan. Et si, comme moi, vous devez faire, parfois, des tests avec des logiciels comme Samba, OpenLDAP, MySQL, Apache, … cette petite astuce est pour vous.</p>
<p>Sur mon netbook j'ai besoin de logiciel serveur pour faire des tests. La majorité du temps, ils démarrent, prolongeant le démarrage ou la sortie de veille, et mangent des cycles processeurs, donc de la batterie et des accès disques, juste pour qu'ils soient disponibles 5 minutes par mois.</p>
<p>Cherchant une solution, j'ai torturé mon esprit dans tous les sens. Puis l'évidence me submergea : les niveaux d'exécution. Un vieux truc dont, il me semble, on ne parle plus tellement : les distributions gèrent tous ça automatiquement et il n'y a aucune raison d'y toucher. On en parle tellement plus que le "future" (systemd) traite ça comme un "legacy concept" (concept hérité du passé). Maintenant on appelle ça des "target".<br />
Mais bon, je préfère écrire <code>init 4</code> que <code>systemctl isolate mon_truc.target</code>. Et quand je vois la documentation de <a href="https://wiki.archlinux.org/index.php/Systemd#Create_custom_target">ArchLinux à propos de systemd pour créer des "target" personalisées</a>, je penses que je vais continuer avec mon héritage du passé.</p>
<p>Dans Debian, le bureau graphique complet, multi-utilisateurs, réseau et tout est au niveau d'exécution 2. Les niveaux 3, 4 et 5 sont identiques au 2. Donc enlevez vos services peu utilisé du niveau 2 et laissez-les dans le niveau 3, exemple :</p>
<pre>
<code class=""># update-rc.d slapd disable 2
</code>
</pre>
<p>Et voilà. Tout ça pour ça ! J'avais dis que c'était évident.</p>
<p>Après, vous pouvez, bien entendu, jouer avec les événements ACPI pour, par exemple, passer au le niveau 3 quand votre netbook est sur le secteur, associer une touche "Fonction" pour passer d'un niveau à l'autre, …</p>
<p>Et il paraît que c'est la tradition : <a href="http://en.wikipedia.org/wiki/File:David_von_Michelangelo.jpg">nimage</a>. C'est pour les femmes et c'est du nu ;-).</p><div><a href="https://linuxfr.org/users/tchetch/journaux/netbook-et-niveau-d-execution.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/95416/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/tchetch/journaux/netbook-et-niveau-d-execution#comments">ouvrir dans le navigateur</a>
</p>
Etienne Bagnoudhttps://linuxfr.org/nodes/95416/comments.atomtag:linuxfr.org,2005:Diary/330032012-08-21T14:08:40+02:002012-08-21T14:08:40+02:00Parlons C, parlons pipe !Licence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<p>Mon livre de chevet, <a href="http://users.powernet.co.uk/eton/unleashed/">Unleashed C</a> (non je mens, mon livre de chevet reste <a href="http://en.wikipedia.org/wiki/Playboy">Playboy</a>, mais ça fait moins sérieux), propose d'implémenter une <a href="http://fr.wikipedia.org/wiki/Fifo">FIFO</a> (ou "pipe", pour tube en anglais) de la façon suivante (approximativement, j'ai simplifié la représentation (surtout il y avait <code>QUEUE</code> écrit et je veux pas de problèmes)) :</p>
<pre>
<code class="">+----------+
| taille |
+----------+
| debut |-------+
+----------+ |
| fin | |
+----------+ V
| +---+---------+
| | s | donnes |
| +---+---------+
| |
| V
| +---+---------+
+------------->| s | donnes |
+---+---------+
|
V
NULL
</code>
</pre>
<p>Avec, bien entendu, plein de code C manipulant des pointeurs (que <a href="http://linuxfr.org/users/tchetch/journaux/et-dieu-inventa-le-soutien-gorge">j'aime</a> :D).</p>
<p>Moi je veux une petite FIFO. Une pouvant contenir 4 caractères (voir 8) ; la version du livre est un peu gore dans ce cas.</p>
<p>Une FIFO, on pousse à droite, ça sort à gauche. On pousse à droite … ça me rappelle un opérateur : <code><<</code>. Je prends une variable, pouvant contenir des valeurs de 4 ou 8 octets, une variable pour compter et j'ai une FIFO. Rudimentaire mais suffisante !</p>
<p>Donc ma FIFO commence sa vie avec une structure :</p>
<pre>
<code class="c"><span class="cm">/* Ne pas déraper, ne pas déraper, ... */</span>
<span class="k">typedef</span> <span class="k">struct</span> <span class="n">s_small_dick</span> <span class="p">{</span> <span class="cm">/* /o\ */</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">count</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">data</span><span class="p">;</span> <span class="cm">/* uint64_t pour 8 caracteres */</span>
<span class="p">}</span> <span class="n">SmallFifo</span><span class="p">;</span>
</code>
</pre>
<p>Ensuite il faut quelques fonctions pour la faire vivre. D'abord l'initialisation (pour la forme) :</p>
<pre>
<code class="c"><span class="kt">void</span> <span class="nf">sf_init</span><span class="p">(</span><span class="n">SmallFifo</span> <span class="o">*</span> <span class="n">f</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span><span class="p">(</span><span class="n">f</span><span class="o">==</span><span class="nb">NULL</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
<span class="n">f</span><span class="o">-></span><span class="n">count</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span>
<span class="n">f</span><span class="o">-></span><span class="n">data</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</code>
</pre>
<p>Ensuite je veux pouvoir ajouter des valeurs. Cette opération est d'une simplicité déconcertante.</p>
<pre>
<code class="c"><span class="kt">void</span> <span class="nf">sf_push</span><span class="p">(</span><span class="n">SmallFifo</span> <span class="o">*</span> <span class="n">f</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">b</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span><span class="p">(</span><span class="n">f</span><span class="o">==</span><span class="nb">NULL</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
<span class="cm">/* C'est une FIFO qui, si elle est pleine, éliminent les valeurs les plus</span>
<span class="cm"> * anciennes (pas conseillé pour la retraite ^^).</span>
<span class="cm"> */</span>
<span class="n">f</span><span class="o">-></span><span class="n">data</span><span class="o">=</span><span class="p">(</span><span class="n">f</span><span class="o">-></span><span class="n">data</span><span class="o"><<</span><span class="mi">8</span><span class="p">)</span><span class="o">|</span><span class="n">b</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span><span class="n">f</span><span class="o">-></span><span class="n">count</span><span class="o"><</span><span class="k">sizeof</span><span class="p">(</span><span class="n">f</span><span class="o">-></span><span class="n">data</span><span class="p">))</span> <span class="n">f</span><span class="o">-></span><span class="n">count</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
</code>
</pre>
<p>Pour récupérer les valeurs, nous allons, très simplement, appliquer la méthode suivante :</p>
<pre>
<code class="c"><span class="kt">unsigned</span> <span class="kt">char</span> <span class="nf">sf_pop</span><span class="p">(</span><span class="n">SmallFifo</span> <span class="o">*</span> <span class="n">f</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span><span class="p">(</span><span class="n">f</span><span class="o">==</span><span class="nb">NULL</span><span class="p">)</span> <span class="k">return</span> <span class="mh">0x00</span><span class="p">;</span>
<span class="cm">/* Pas tomber trop bas */</span>
<span class="k">if</span><span class="p">(</span><span class="n">f</span><span class="o">-></span><span class="n">count</span><span class="o">></span><span class="mi">0</span><span class="p">)</span> <span class="n">f</span><span class="o">-></span><span class="n">count</span><span class="o">--</span><span class="p">;</span>
<span class="k">return</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="p">)(</span><span class="n">f</span><span class="o">-></span><span class="n">data</span><span class="o">>></span><span class="p">(</span><span class="mi">8</span> <span class="o">*</span> <span class="n">f</span><span class="o">-></span><span class="n">count</span><span class="p">))</span> <span class="o">&</span> <span class="mh">0xFF</span><span class="p">;</span>
<span class="p">}</span>
</code>
</pre>
<p>Et, pour la forme, une fonction qui retourne une valeur différente de 0 s'il y a encore des données dans notre FIFO :</p>
<pre>
<code class="c"><span class="kt">unsigned</span> <span class="kt">char</span> <span class="nf">sf_has_data</span><span class="p">(</span><span class="n">SmallFifo</span> <span class="o">*</span> <span class="n">f</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span><span class="p">(</span><span class="n">f</span><span class="o">==</span><span class="nb">NULL</span><span class="p">)</span> <span class="k">return</span> <span class="mh">0x00</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span><span class="n">f</span><span class="o">-></span><span class="n">count</span><span class="o">></span><span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="mh">0xEB</span><span class="p">;</span> <span class="cm">/* mes initiales \o/ */</span>
<span class="k">return</span> <span class="mh">0x00</span><span class="p">;</span>
<span class="p">}</span>
</code>
</pre>
<p>Bien entendu, ce code n'a été que brièvement testé et contient, peut-être, des erreurs.</p>
<pre>
<code class="c"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span> <span class="n">argv</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">SmallFifo</span> <span class="n">ma_fifo</span><span class="p">;</span>
<span class="kt">char</span> <span class="n">des_valeurs</span><span class="p">[]</span><span class="o">=</span><span class="s">"abcdefghijklmnopqrstuvwxyz"</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="cm">/* un compteur */</span>
<span class="n">sf_init</span><span class="p">(</span><span class="o">&</span><span class="n">ma_fifo</span><span class="p">);</span>
<span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span><span class="n">i</span><span class="o"><</span><span class="mi">4</span><span class="p">;</span><span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="n">sf_push</span><span class="p">(</span><span class="o">&</span><span class="n">ma_fifo</span><span class="p">,</span> <span class="n">des_valeurs</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
<span class="p">}</span>
<span class="k">while</span><span class="p">(</span><span class="n">sf_has_data</span><span class="p">(</span><span class="o">&</span><span class="n">ma_fifo</span><span class="p">))</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"%c "</span><span class="p">,</span> <span class="n">sf_pop</span><span class="p">(</span><span class="o">&</span><span class="n">ma_fifo</span><span class="p">));</span>
<span class="p">}</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span><span class="n">i</span><span class="o"><</span><span class="mi">26</span><span class="p">;</span><span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="n">sf_push</span><span class="p">(</span><span class="o">&</span><span class="n">ma_fifo</span><span class="p">,</span> <span class="n">des_valeurs</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
<span class="p">}</span>
<span class="k">while</span><span class="p">(</span><span class="n">sf_has_data</span><span class="p">(</span><span class="o">&</span><span class="n">ma_fifo</span><span class="p">))</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"%c "</span><span class="p">,</span> <span class="n">sf_pop</span><span class="p">(</span><span class="o">&</span><span class="n">ma_fifo</span><span class="p">));</span>
<span class="p">}</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code>
</pre>
<p>Pour tester ce code, n'oubliez pas d'inclure <code>stdint.h</code> ou alors changer le type en <code>unsigned long int</code>, ça devrait faire 4 octets même sur un <a href="http://en.wikipedia.org/wiki/Atmel_AVR">AVR</a> ou un <a href="http://en.wikipedia.org/wiki/PIC_microcontroller">PIC</a>.</p>
<p>PS: Si, par mégarde, j'ai choqué une femme dans ce journal, j'en suis désolé. Pour me faire pardonner, j'accepterais jusqu'aux châtiments corporels, y compris être tuer et violer tant que ça reste dans cet ordre là (sauf ma copine, <code>halle_berry.jpeg</code>, qui peut le faire dans l'ordre inverse).</p><div><a href="https://linuxfr.org/users/tchetch/journaux/parlons-c-parlons-pipe.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/95267/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/tchetch/journaux/parlons-c-parlons-pipe#comments">ouvrir dans le navigateur</a>
</p>
Etienne Bagnoudhttps://linuxfr.org/nodes/95267/comments.atomtag:linuxfr.org,2005:Diary/329862012-08-17T17:23:22+02:002012-08-17T17:23:22+02:00Et Dieu inventa le soutien gorge !Licence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<p>Le C est connu pour ses pointeurs. Les pointeurs sont une merveille pour certains, une horreur pour d'autre. Je sais qu'il s'agit d'un nième débat religieux par ici, mais parlons de C et de pointeurs !</p>
<p>Un vrai moment de détente pour le week-end :D</p>
<p>Le noyau Linux utilise une forme particulière de listes chaînées qui nous permet d'apprécier ce genre de code <a href="http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=blob;f=include/linux/kernel.h;h=604382143bcfccd6772260ed56e16589800af83c;hb=HEAD">include/linux/kernel.h:683</a>:</p>
<pre>
<code class="c"><span class="cp">#define container_of(ptr, type, member) ({ \</span>
<span class="cp"> const typeof( ((type *)0)->member ) *__mptr = (ptr); \</span>
<span class="cp"> (type *)( (char *)__mptr - offsetof(type,member) );})</span>
</code>
</pre>
<p>Et quand mon apprenti tombe sur ce genre de truc, je me dois de lui expliquer.<br />
Pour ceux qui se posent la question, ce petit code permet de trouver l'adresse d'une structure à l'aide de l'adresse d'un membre de la structure.</p>
<p>Imaginons la structure suivante :</p>
<pre>
<code class="c"><span class="k">struct</span> <span class="n">x</span> <span class="p">{</span>
<span class="kt">int</span> <span class="n">a</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">b</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">struct</span> <span class="n">x</span> <span class="n">X</span><span class="p">;</span>
</code>
</pre>
<p>Et admettons :</p>
<ul><li>X est à l'adresse <code>0x14C4</code></li>
<li>X.a est à l'adresse <code>0x14C8</code></li>
<li>X.b est à l'adresse <code>0x14CC</code></li>
</ul><p>Donc, si ma structure X était à l'adresse <code>0x0000</code>, nous aurions :</p>
<ul><li>X.a à l'adresse <code>0x0004</code></li>
<li>X.b à l'adresse <code>0x0008</code></li>
</ul><p>Donc si j'ai l'adresse de X.a, j'ai l'adresse de X, car :</p>
<pre>
<code class="c"><span class="o">&</span><span class="n">X</span> <span class="o">==</span> <span class="o">&</span><span class="p">(</span><span class="n">X</span><span class="p">.</span><span class="n">a</span><span class="p">)</span> <span class="o">-</span> <span class="p">((</span><span class="k">struct</span> <span class="n">x</span> <span class="o">*</span><span class="p">)</span><span class="mi">0</span><span class="p">)</span><span class="o">-></span><span class="n">a</span><span class="p">;</span>
</code>
</pre>
<p>Tout ceci est <a href="http://c-faq.com/struct/offsetof.html">bien connu</a> et est disponible dans <code>stddef.h</code> à l'aide de la macro <code>offsetof</code> :</p>
<pre>
<code class="c"><span class="cp">#include <stddef.h></span>
<span class="n">offsetof</span><span class="p">(</span><span class="k">struct</span> <span class="n">x</span><span class="p">,</span> <span class="n">a</span><span class="p">);</span>
</code>
</pre>
<p>Tous ces éléments mis ensemble nous permettent d'obtenir des structures de données un peu différentes :</p>
<pre>
<code class="c"><span class="k">struct</span> <span class="n">s_list</span> <span class="p">{</span>
<span class="k">struct</span> <span class="n">s_list</span> <span class="o">*</span> <span class="n">next</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">s_list</span> <span class="o">*</span> <span class="n">previous</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">struct</span> <span class="n">s_structure_x</span> <span class="p">{</span>
<span class="cm">/* membres */</span>
<span class="k">struct</span> <span class="n">s_list</span> <span class="n">list</span><span class="p">;</span>
<span class="p">};</span>
</code>
</pre>
<p>En lieu et place de :</p>
<pre>
<code class="c"><span class="k">struct</span> <span class="n">s_structure_x</span> <span class="p">{</span>
<span class="cm">/* membres */</span>
<span class="k">struct</span> <span class="n">s_structure_x</span> <span class="o">*</span> <span class="n">next</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">s_structure_x</span> <span class="o">*</span> <span class="n">previous</span><span class="p">;</span>
<span class="p">};</span>
</code>
</pre>
<p>ou encore :</p>
<pre>
<code class="c"><span class="k">struct</span> <span class="n">s_list</span> <span class="p">{</span>
<span class="kt">void</span> <span class="o">*</span> <span class="n">data</span><span class="p">;</span>
<span class="kt">size_t</span> <span class="n">length</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">type</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">s_list</span> <span class="o">*</span> <span class="n">next</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">s_list</span> <span class="o">*</span> <span class="n">previous</span><span class="p">;</span>
<span class="p">};</span>
</code>
</pre>
<p>et variantes.</p>
<p>Voilà, je suis pas certain d'être clair, mais je fais vite car ce n'est pas le sujet. En expliquant ce sujet, une idée m'est venue (et ça c'est tout l'intérêt de former des jeunes) pour mon projet actuel. Dans les grandes lignes, le projet est de faire communiquer des appareils entre eux.<br />
Afin de faire ça proprement, j'ai, bien entendu, défini quelques couches, 3 en l'occurrence et chacune encapsule l'autre. Un couche réseau, une couche application et une couche donnée.</p>
<p>Dans la réflexion, le fait de travailler sur des micro-contrôleurs n'ayant que 512 octets de mémoires RAM et que la quantité de données transmises peut atteindre 150 octets, plus quelques 10 octets pour les couches, un encodage des données qui ajoutent une 20aine d'octets (1 bit perdu par octet transmis) et quelques variables d'état, l'utilisation de mémoire tampon pour le réseau est à proscrire ; on arrive presque à la moitié de la RAM juste pour les communications (dont la majeure partie n'est que ce qui se trouve déjà en mémoire mais sous une autre forme).</p>
<p>Le code réseau est donc prévu pour travailler en flux. Les données sont transformées et transmises à mesure. Quelques variables d'états plus tard, le réseau ne coûte qu'une ou deux dizaines d'octets en mémoire.</p>
<p>Le problème vient de l'enchaînement des couches dans le code, par exemple :</p>
<pre>
<code class="c"><span class="kt">void</span> <span class="nf">hw_send</span><span class="p">(</span> <span class="cm">/* ... */</span> <span class="p">)</span> <span class="p">{</span>
<span class="cm">/* ... */</span>
<span class="n">l1_send</span><span class="p">();</span>
<span class="cm">/* ... */</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">l1_send</span><span class="p">(</span> <span class="cm">/* ... */</span> <span class="p">)</span> <span class="p">{</span>
<span class="cm">/* ... */</span>
<span class="n">l2_send</span><span class="p">();</span>
<span class="cm">/* ... */</span>
<span class="p">}</span>
<span class="cm">/* ... */</span>
</code>
</pre>
<p>Ça marche, mais si je veux, par exemple, ajouter un traitement (chiffrement ?) entre la couche 1 et 2, le code doit être modifié de manière dramatique. Ou si je veux réutiliser la couche 2 dans une couche 1bis. Non ce qu'il me faut, c'est une sorte de liste chaînée qui traversent les couches et qui me permettent, le cas échéant, d'intercaler un traitement particulier.</p>
<p>Et du noyau vint la solution qui me semble, pour l'instant, la plus intéressante (on verra si ça tient jusqu'à lundi (dans mon esprit (dégénéré))). J'ai fais un petit test et ça donne ça :</p>
<pre>
<code class="c"><span class="cp">#include <stdio.h></span>
<span class="cp">#include <string.h></span>
<span class="cp">#include <stddef.h></span>
<span class="cm">/* Structure pour mes fonctions de communications </span>
<span class="cm"> *</span>
<span class="cm"> * send et receive reçoivent en premier paramètre une variable </span>
<span class="cm"> * (ComFunctions *). Le deuxième paramètre de send est une variable d'état,</span>
<span class="cm"> * quand elle est à 0xFF, il n'y a plus rien à envoyer. Pour receive, il </span>
<span class="cm"> * s'agit de l'octet reçu par le réseau.</span>
<span class="cm"> * send retourne l'octet à transmettre et receive l'état qui, une fois à</span>
<span class="cm"> * 0xFF indique qu'il n'y a plus rien à recevoir.</span>
<span class="cm"> * La variable lower contient l'adresse de la couche en-dessous de l'actuelle.</span>
<span class="cm"> */</span>
<span class="k">typedef</span> <span class="k">struct</span> <span class="n">s_com_funcs</span> <span class="n">ComFunctions</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">s_com_funcs</span> <span class="p">{</span>
<span class="n">ComFunctions</span> <span class="o">*</span> <span class="n">lower</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="p">(</span><span class="o">*</span><span class="n">send</span><span class="p">)(</span><span class="kt">void</span> <span class="o">*</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="p">);</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="p">(</span><span class="o">*</span><span class="n">receive</span><span class="p">)(</span><span class="kt">void</span> <span class="o">*</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">char</span><span class="p">);</span>
<span class="p">};</span>
<span class="cm">/* Mes structures avec les valeurs nécessaires pour chaque couche ainsi</span>
<span class="cm"> * qu'une instance de ComFunctions.</span>
<span class="cm"> */</span>
<span class="k">struct</span> <span class="n">l1</span> <span class="p">{</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">val1</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">val2</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">val3</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">state</span><span class="p">;</span>
<span class="n">ComFunctions</span> <span class="n">f</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">struct</span> <span class="n">l2</span> <span class="p">{</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">val1</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">val2</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">state</span><span class="p">;</span>
<span class="n">ComFunctions</span> <span class="n">f</span><span class="p">;</span>
<span class="p">};</span>
<span class="cm">/* La fonction send. En paramètre, nous avons la couche la plus haute,</span>
<span class="cm"> * retourne 0 quand la transmission est terminée (pour permettre de faire</span>
<span class="cm"> * while(send(...));)</span>
<span class="cm"> */</span>
<span class="kt">char</span> <span class="nf">send</span><span class="p">(</span><span class="n">ComFunctions</span> <span class="o">*</span> <span class="n">highest</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">res</span><span class="o">=</span><span class="mh">0x00</span><span class="p">;</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"0x%02X "</span><span class="p">,</span> <span class="n">highest</span><span class="o">-></span><span class="n">send</span><span class="p">(</span><span class="n">highest</span><span class="p">,</span> <span class="o">&</span><span class="n">res</span><span class="p">));</span>
<span class="k">if</span><span class="p">(</span><span class="n">res</span><span class="o">==</span><span class="mh">0xFF</span><span class="p">)</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/* Des init bidons, juste pour le test */</span>
<span class="kt">void</span> <span class="nf">init_l2</span><span class="p">(</span><span class="k">struct</span> <span class="n">l2</span> <span class="o">*</span> <span class="n">me</span><span class="p">)</span> <span class="p">{</span> <span class="n">me</span><span class="o">-></span><span class="n">val1</span><span class="o">=</span><span class="sc">'Z'</span><span class="p">;</span> <span class="n">me</span><span class="o">-></span><span class="n">val2</span><span class="o">=</span><span class="sc">'F'</span><span class="p">;</span> <span class="n">me</span><span class="o">-></span><span class="n">state</span><span class="o">=</span><span class="mh">0x00</span><span class="p">;</span> <span class="p">}</span>
<span class="kt">void</span> <span class="nf">init_l1</span><span class="p">(</span><span class="k">struct</span> <span class="n">l1</span> <span class="o">*</span> <span class="n">me</span><span class="p">)</span> <span class="p">{</span> <span class="n">me</span><span class="o">-></span><span class="n">val1</span><span class="o">=</span><span class="sc">'a'</span><span class="p">;</span> <span class="n">me</span><span class="o">-></span><span class="n">val2</span><span class="o">=</span><span class="sc">'b'</span><span class="p">;</span> <span class="n">me</span><span class="o">-></span><span class="n">val3</span><span class="o">=</span><span class="sc">'c'</span><span class="p">;</span>
<span class="n">me</span><span class="o">-></span><span class="n">state</span><span class="o">=</span><span class="mh">0x00</span><span class="p">;</span> <span class="p">}</span>
<span class="cm">/* Des fonctions send, juste un aperçu, mais le code est trivial */</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="nf">send_l2</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span> <span class="n">com_funcs</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span> <span class="n">state</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">struct</span> <span class="n">l2</span> <span class="o">*</span> <span class="n">me</span><span class="o">=</span><span class="p">(</span><span class="k">struct</span> <span class="n">l2</span> <span class="o">*</span><span class="p">)((</span><span class="n">com_funcs</span><span class="p">)</span><span class="o">-</span><span class="n">offsetof</span><span class="p">(</span><span class="k">struct</span> <span class="n">l2</span><span class="p">,</span> <span class="n">f</span><span class="p">));</span>
<span class="cm">/* ... */</span>
<span class="p">}</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="nf">send_l1</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span> <span class="n">com_funcs</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span> <span class="n">state</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">struct</span> <span class="n">l1</span> <span class="o">*</span> <span class="n">me</span><span class="o">=</span><span class="p">(</span><span class="k">struct</span> <span class="n">l1</span> <span class="o">*</span><span class="p">)((</span><span class="n">com_funcs</span><span class="p">)</span><span class="o">-</span><span class="n">offsetof</span><span class="p">(</span><span class="k">struct</span> <span class="n">l1</span><span class="p">,</span> <span class="n">f</span><span class="p">));</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">lower_state</span><span class="o">=</span><span class="mh">0x00</span><span class="p">,</span> <span class="n">tmp</span><span class="o">=</span><span class="mh">0x00</span><span class="p">;</span>
<span class="k">switch</span><span class="p">(</span><span class="n">me</span><span class="o">-></span><span class="n">state</span><span class="p">)</span> <span class="p">{</span>
<span class="cm">/* ... plein de code ... */</span>
<span class="k">case</span> <span class="mi">2</span>:
<span class="n">tmp</span><span class="o">=</span><span class="sc">'!'</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span><span class="n">me</span><span class="o">-></span><span class="n">f</span><span class="p">.</span><span class="n">lower</span><span class="o">==</span><span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
<span class="n">me</span><span class="o">-></span><span class="n">state</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">tmp</span><span class="o">=</span><span class="n">me</span><span class="o">-></span><span class="n">f</span><span class="p">.</span><span class="n">lower</span><span class="o">-></span><span class="n">send</span><span class="p">(</span><span class="n">me</span><span class="o">-></span><span class="n">f</span><span class="p">.</span><span class="n">lower</span><span class="p">,</span> <span class="o">&</span><span class="n">lower_state</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span><span class="n">lower_state</span><span class="o">==</span><span class="mh">0xFF</span><span class="p">)</span> <span class="n">me</span><span class="o">-></span><span class="n">state</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="o">*</span><span class="n">state</span><span class="o">=</span><span class="n">me</span><span class="o">-></span><span class="n">state</span><span class="p">;</span>
<span class="k">return</span> <span class="n">tmp</span><span class="p">;</span>
<span class="cm">/* ... encore plein de code ... */</span>
<span class="p">}</span>
<span class="cm">/* Une fonction main */</span>
<span class="kt">int</span> <span class="n">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span> <span class="n">argv</span><span class="p">)</span>
<span class="p">{</span>
<span class="cm">/* Que deux couches, trop flemmard pour la troisième ^^ */</span>
<span class="k">struct</span> <span class="n">l1</span> <span class="n">x</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">l2</span> <span class="n">y</span><span class="p">;</span>
<span class="n">init_l1</span><span class="p">(</span><span class="o">&</span><span class="n">x</span><span class="p">);</span>
<span class="n">init_l2</span><span class="p">(</span><span class="o">&</span><span class="n">y</span><span class="p">);</span>
<span class="n">x</span><span class="p">.</span><span class="n">f</span><span class="p">.</span><span class="n">lower</span><span class="o">=&</span><span class="p">(</span><span class="n">y</span><span class="p">.</span><span class="n">f</span><span class="p">);</span>
<span class="n">x</span><span class="p">.</span><span class="n">f</span><span class="p">.</span><span class="n">send</span><span class="o">=</span><span class="n">send_l1</span><span class="p">;</span>
<span class="n">y</span><span class="p">.</span><span class="n">f</span><span class="p">.</span><span class="n">lower</span><span class="o">=</span><span class="nb">NULL</span><span class="p">;</span>
<span class="n">y</span><span class="p">.</span><span class="n">f</span><span class="p">.</span><span class="n">send</span><span class="o">=</span><span class="n">send_l2</span><span class="p">;</span>
<span class="k">while</span><span class="p">(</span><span class="n">send</span><span class="p">(</span><span class="o">&</span><span class="p">(</span><span class="n">x</span><span class="p">.</span><span class="n">f</span><span class="p">)));</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code>
</pre>
<p>Donc le fonctionnement est très simple, chaque couche peut appeler la couche sous-jacente et traiter son résultat avant de l'intégrer dans sa propre sortie. Ça permet d'intégrer des filtres entre les couches (j'ai testé avec un chiffrement fort (xor :-p), c'est démentiel).<br />
Rien de bien révolutionnaire (déjà je travail pas chez Apple, c'est donc mal parti) et certainement déjà utilisé dans bien des projets, mais je cherchais juste une excuse pour faire un journal sur les pointeurs C (et les pointeurs de fonctions). Et ça fait jamais de mal de revoir un peu de C :)</p>
<p>Ah ! et je voulais aussi que ça ce sache : j'aime quand ça pointe !</p><div><a href="https://linuxfr.org/users/tchetch/journaux/et-dieu-inventa-le-soutien-gorge.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/95222/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/tchetch/journaux/et-dieu-inventa-le-soutien-gorge#comments">ouvrir dans le navigateur</a>
</p>
Etienne Bagnoudhttps://linuxfr.org/nodes/95222/comments.atomtag:linuxfr.org,2005:News/291682012-02-17T12:58:10+01:002012-02-17T13:57:48+01:00Fossil, une forge pour DVCS<div><p><a href="http://en.wikipedia.org/wiki/Fossil_(file_system)">Fossil</a> est <a href="http://fr.wikipedia.org/wiki/Syst%C3%A8me_de_fichiers">le système de fichiers</a> de <a href="http://fr.wikipedia.org/wiki/Plan_9_from_Bell_Labs">Plan9</a>. Ce n'est pas le sujet de cete dépêche.</p>
<p><a href="http://fossil-scm.org">Fossil</a> c'est aussi un outil de gestion de version décentralisé, <a href="http://en.wikipedia.org/wiki/Distributed_version_control_system">DCVS</a> en court. Il est toujours un peu osé, par les temps qui courent, de parler d'un autre DCVS que le très apprécié <a href="http://fr.wikipedia.org/wiki/Git">Git</a>, mais Fossil c'est aussi un peu plus que ça ; un plus qui m'a beaucoup séduit.</p>
<p>Fossil c'est aussi un wiki, un outil de gestion de tickets et une interface Web (et son serveur) dans un seul exécutable. Sans entrer dans les détails, il prend en charge les mêmes fonctionnalités que la plus grande partie des DCVS. Il se veut robuste et fiable, simple, un protocole réseau simple (HTTP) rendu suffisamment efficace pour fonctionner sur une ligne téléphonique 56k et facile d'utilisation (pas de configuration, commande simple). Ça c'est la partie "marketing".</p>
<p>Si la description sonne un peu comme celle de <a href="http://www.hwaci.com/sw/sqlite/index.html">SQLite</a>, ce n'est pas un hasard : Fossil est développé par les mêmes personnes, utilise SQLite pour le stockage et est utilisé comme gestionnaire de versions pour ce projet (et d'autres). Fossil n'est donc pas juste un projet sombre dans un coin du Net.</p>
<p>
<em>NdM : merci à Etienne Bagnoud pour son journal.</em>
</p></div><ul><li>lien nᵒ 1 : <a title="http://linuxfr.org/users/tchetch/journaux/fossil" hreflang="fr" href="https://linuxfr.org/redirect/75354">Journal à l'origine de la dépêche</a></li><li>lien nᵒ 2 : <a title="http://fossil-scm.org" hreflang="en" href="https://linuxfr.org/redirect/75355">Présentation de fossil</a></li></ul><div><p>Ce qui m'a séduit c'est d'avoir tout cet attirail de fonctionnalités dans un exécutable de ~800 Kio. Depuis quelques temps, je n'utilise plus qu'un netbook dont le seul critère est l'autonomie. Il y a aussi que je n'ai plus de connexion Internet à mon domicile, mais un abonnement de téléphonie mobile avec données illimitées (limitation de la bande passante à partir de 12 Go/mois). Je recherche donc des outils utilisant un minimum Internet, légers et accessibles sur demande. Fossil est cet outil. Pas besoin de serveur Apache, ou autre, tournant sur ma machine, peu puissante, pour écrire dans un wiki et un système de tickets. Pas besoin d'accès Internet non plus. Un simple <code>fossil ui nom_du_depot</code> et mon navigateur s'ouvre automatiquement sur le wiki et la gestion de ticket du projet. <code>Ctrl-C</code> et tout s'arrête.</p>
<p>Le dépôt, un seul fichier SQLite. Je le copie autre part et j'ai mon projet, avec toutes ses versions, le wiki et les tickets : la sauvegarde est simplifiée.</p>
<p>Pour la partie distribuée de l'outil, je n'ai pas encore testé. Mais les fonctionnalités sont là, Fossil peut tourner en CGI dans un serveur Web plus complet ou fournir son propre serveur (et peu être utilisé depuis inetd). Il a les fonctionnalités "push", "pull", "clone" et "update", mais peut aussi fonctionner en synchronisation automatique, comme un CVS ou un SVN.</p>
<p>Bien entendu, il gère l'importation et l'exportation vers Git, sa prise en main est immédiate (incomparable par rapport à Git) et il y a une gestion d'utilisateurs et de droits très bien faite et complète.</p>
<p>Sur la page de comparaison entre <a href="http://fossil-scm.org/index.html/doc/trunk/www/fossil-v-git.wiki">Fossil et GIT</a>, on y trouve la phrase suivante :</p>
<blockquote>
<p>The Git model works best for large projects, like the Linux kernel for which Git was designed.</p>
</blockquote>
<p>"Le modèle Git est idéal pour des grands projet, comme le noyau Linux pour lequel il a été conçu". </p>
<p>Fossil a vraiment été conçu pour un développeur seul ou une petite équipe ne voulant pas se prendre la tête avec l'administration d'un serveur complet pour héberger trois outils simples (wiki, ticket et DCVS). Et dans ce domaine, Fossil semble vraiment être un réussite.</p>
<p>Je pense que ce projet peut en intéresser plus d'un, je vous invite donc à l'essayer (surtout que sa simplicité déconcertante invite vraiment à le tester). </p></div><div><a href="https://linuxfr.org/news/fossil-une-forge-pour-dvcs.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/89516/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/fossil-une-forge-pour-dvcs#comments">ouvrir dans le navigateur</a>
</p>
Etienne BagnoudNeoXbaud123tuiu polNÿcopatrick_ghttps://linuxfr.org/nodes/89516/comments.atomtag:linuxfr.org,2005:Diary/322012012-02-17T10:58:13+01:002012-02-17T10:58:13+01:00FossilLicence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<p><a href="http://en.wikipedia.org/wiki/Fossil_(file_system)">Fossil</a> est le système de fichier de <a href="http://fr.wikipedia.org/wiki/Plan_9_from_Bell_Labs">Plan9</a>. Ce n'est pas le sujet de ce journal.</p>
<p><a href="http://fossil-scm.org">Fossil</a> c'est aussi un outil de gestion de version décentralisé, <a href="http://en.wikipedia.org/wiki/Distributed_version_control_system">DCVS</a> en court. Il est toujours un peu osé, par les temps qui court, de parler d'un autre DCVS que le très apprécié <a href="http://fr.wikipedia.org/wiki/Git">Git</a>, mais Fossil c'est aussi un peu plus que ça ; un plus qui m'a beaucoup séduit.</p>
<p>Fossil c'est aussi un wiki, un outil de gestion de ticket et une interface Web (et son serveur) dans un seul exécutable. Sans entrer dans les détails, il supporte les mêmes fonctionnalités que la plus grande partie des DCVS, il se veut robuste et fiable, simple, un protocole réseau simple (HTTP) rendu suffisamment efficace pour fonctionner sur un ligne téléphonique 56k et facile d'utilisation (pas de configuration, commande simple). Ça c'est la partie "marketing".<br />
Si la description sonne un peu comme celle de <a href="http://www.hwaci.com/sw/sqlite/index.html">SQLite</a>, ce n'est pas un hasard : Fossil est développé par les mêmes personnes, utilise SQLite pour le stockage et est utilisé comme gestionnaire de version pour ce projet (et d'autres). Fossil n'est donc pas juste un projet sombre dans un coin du Net.</p>
<p>Ce qui m'a séduit c'est d'avoir tout cet attirail de fonctionnalités dans un exécutable de ~800KiB. Depuis quelques temps, je n'utilise plus qu'un netbook dont le seul critère est l'autonomie. J'ai aussi plus de connexion Internet à mon domicile, mais un abonnement de téléphonie mobile avec données illimitées (limitation de la bande passante à partir de 12G/mois). Je recherche donc des outils utilisant un minimum Internet, léger et accessible sur demande. Fossil est cet outil. Pas besoin de serveur Apache, ou autre, tournant sur ma machine, peu puissante, pour écrire dans un wiki et un système de ticket. Pas besoin d'accès Internet non plus. Un simple <code>fossil ui nom_du_depot</code> et mon navigateur s'ouvre automatiquement sur le wiki et la gestion de ticket du projet. <code>Ctrl-C</code> et tout s'arrête.<br />
Le dépôt, un seul fichier SQLite. Je le copie autre part et j'ai mon projet, avec toutes ses versions, le wiki et les tickets : la sauvegarde est simplifiée.</p>
<p>Pour la partie distribuée de l'outil, je n'ai pas encore testé. Mais les fonctionnalités sont là, Fossil peut tourner en CGI dans un serveur Web plus complet ou fournir son propre serveur (et peu être utilisé depuis inetd). Il a les fonctionnalités "push", "pull", "clone" et "update", mais peu aussi fonctionner en synchronisation automatique, comme un CVS ou un SVN.</p>
<p>Bien entendu, il supporte l'importation et l'exportation vers Git, sa prise en main est immédiate (incomparable par rapport à Git) et il y'a une gestion d'utilisateur et de droit très bien faites et complètes.</p>
<p>Sur la page de comparaison entre <a href="http://fossil-scm.org/index.html/doc/trunk/www/fossil-v-git.wiki">Fossil et GIT</a>, on y trouve la phrase suivante :</p>
<blockquote>
<p>The Git model works best for large projects, like the Linux kernel for which Git was designed.</p>
</blockquote>
<p>"Le modèle Git est idéal pour des grands projet, comme le noyau Linux pour lequel il a été conçu". Fossil a vraiment été conçu pour un développeur seul ou une petite équipe ne voulant pas se prendre la tête avec l'administration d'un serveur complet pour héberger trois outils simples (wiki, ticket et DCVS). Et dans ce domaine, Fossil semble vraiment être un réussite.</p>
<p>Je pense que ce projet peut intéressé plus d'un, je vous invite donc à l'essayer (surtout que sa simplicité déconcertante invite vraiment à le tester). </p><div><a href="https://linuxfr.org/users/tchetch/journaux/fossil.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/89514/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/tchetch/journaux/fossil#comments">ouvrir dans le navigateur</a>
</p>
Etienne Bagnoudhttps://linuxfr.org/nodes/89514/comments.atomtag:linuxfr.org,2005:Diary/320712012-01-20T17:31:05+01:002012-01-20T17:31:05+01:00Internet.jpg, drogue et rock'n'rollLicence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<p>Encore un drame sur Internet : Megaupload ferme, Anonymous attaque, <a href="http://www.google.com/hostednews/ap/article/ALeqM5g0HiB0PrdprLqIHlwUdYtB05l2sA">l'EFF dénonce</a> et la MPAA rappelle qu'elle défend la liberté d'expression sur Internet (même lien que pour l'EFF). Voilà ce que j'ai retenu des articles sur le sujet.<br />
Les chiffres indiquent plus de <a href="http://www.wikipedia.org/wiki/Megaupload">180 millions d'utilisateurs</a> jetés à la rue. Le plus ironique : quelques heures après les protestations contre SOPA (comprendre "le gouvernement se torche avec vos protestations" ?)</p>
<p>J'ai appris la nouvelle, après avoir perdu trois parties de <a href="http://www.wikipedia.org/wiki/Beer_Pong">Beer Pong</a> (après une journée à configurer <a href="http://www.bacula.org">Bacula</a>, il faut au moins ça), par des gens pleurant sur <a href="http://www.encyclopediadramatica.ch/Facebook">Facebook</a>. Ma réaction a oscillé entre "Ouf, youporn, redtube, xtube, porntube, ... sont épargnés ... je vais bien dormir ce soir" et "Bah il reste le P2P ou <a href="http://www.wikipedia.org/wiki/Sneaker_net">Sneakernet</a>". J'ai bien dormi, en<br />
cause : ma défaite au Beer pong.</p>
<p>On sous-estime trop souvent Sneakernet dans l'échange de fichier. Pourtant c'est un moyen anonyme et fiable d'échange de fichiers. C'est même plus que ça, c'est le moyen le <a href="http://xkcd.com/949/">plus simple</a>. Avec un séparation entre les gens de <a href="http://www.nytimes.com/2011/11/22/technology/between-you-and-me-4-74-degrees.html">4.74 degrés</a>, il devient très possible de transférer une grande quantité de données rapidement (enfin avec une forte latence) à travers le monde entier. Facebook pourrait même<br />
devenir la première plate-forme d'échange de fichiers illégaux ... Je vois d'ici les titres "Anonymous downs FBI after Facebook shutdown". Délectable.</p>
<p>Plus je réfléchis, plus c'est possible. Vous vous souvenez de la <a href="http://tools.ietf.org/html/rfc5514">RFC 5514</a>, IPv6 over Social Networks ? <a href="http://apps.facebook.com/ipoverfb/">Une application Facebook implémente actuellement cette norme</a>. Pourquoi pas un "FTP over Social Network" ? Au lieu d'utiliser les mentions "j'aime" pour annonce<br />
r ce qu'on aime, on les utiliserait pour annoncer ce que l'on possède. Les outils de localisation pour savoir où aller avec son disque dur, ... Tout est là ...</p>
<p>Moi j'y crois au "FTP over Social Network", si j'osais, j'écrirais même une RFC pour le 1er avril 2012 !</p>
<p>PS: je suis pas peu fier du titre, surtout qu'il y en a qui ne savent même pas que le porno, dans le temps, n'était que des ".jpg". Maintenant c'est des vidéos HD en streaming, du coups les vieux barbus, frustrés, se sont mis à la <a href="http://www.gnu.org/software/hurd/hurd.html">masturbation intellectuelle</a> :) </p><div><a href="https://linuxfr.org/users/tchetch/journaux/internetjpg-drogue-et-rocknroll.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/89109/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/tchetch/journaux/internetjpg-drogue-et-rocknroll#comments">ouvrir dans le navigateur</a>
</p>
Etienne Bagnoudhttps://linuxfr.org/nodes/89109/comments.atomtag:linuxfr.org,2005:Diary/313772011-07-15T12:36:18+02:002011-07-15T12:36:18+02:00Debian GNU/Hurd pour Wheezy ?Licence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<p>D'après les <a href="http://www.gnu.org/software/hurd/news/2011-q2.html">dernières nouvelles du projet Debian GNU/Hurd</a>, on pourrait s'attendre à voir la sortie de officielle vers fin 2012, date estimée pour Debian 7.0 aka Wheezy.</p>
<blockquote>
<p>On the organizational side, there is now a real plan to release a Hurd variant of Debian with their next major release, Wheezy. </p>
</blockquote>
<p><em>Au niveau organisationnelle, nous pouvons planifier une variante de Debian basée sur Hurd pour la prochaine sortie majeure : Wheezy.</em></p>
<p>Si le noyau <a href="http://fr.wikipedia.org/wiki/Hurd">Hurd</a> sort effectivement avec Debian 7, cela sera le troisième noyau supporté par la distribution historique après Linux et FreeBSD, respectivement <a href="http://www.debian.org/">Debian GNU/Linux</a> (depuis 1993) et <a href="http://www.debian.org/ports/kfreebsd-gnu/">Debian GNU/kFreeBSD</a> (depuis 2011).</p>
<p><a href="http://www.debian.org/News/2011/20110205a">Debian 6.0 aka Squeeze</a>, la dernière version sortie, est disponible sur 9 architectures matérielles pour la variante GNU/Linux et 2 architectures matérielles (i386 et amd64) pour la variante GNU/kFreeBSD.</p>
<p>Ça va apporter de l'eau au moulin de ceux qui prétendent que Debian GNU/kFreeBSD est un <a href="https://linuxfr.org/news/un-entretien-avec-lennart-poettering">"toy OS"</a>, en même temps <a href="http://www.crn.com/news/applications-os/18813680/linux-debate.htm">"[...] Linux is a toy"</a>.</p><div><a href="https://linuxfr.org/users/tchetch/journaux/debian-gnuhurd-pour-wheezy.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/86813/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/tchetch/journaux/debian-gnuhurd-pour-wheezy#comments">ouvrir dans le navigateur</a>
</p>
Etienne Bagnoudhttps://linuxfr.org/nodes/86813/comments.atomtag:linuxfr.org,2005:Diary/313592011-07-11T13:00:22+02:002011-07-11T13:00:22+02:00Google censure !Licence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<p>Pour des raisons de sécurités, évidemment. D'après cet article du <a href="http://www.theregister.co.uk/2011/07/06/google_cans_11m_dot_co_dot_cc_sites/">Register</a>, tous les sous-domaines de co.cc auraient été supprimés des résultats du moteur de recherche. Ce fournisseur de services Internet serait utilisé par des programmes malicieux.</p>
<p>Protéger le pauvre utilisateur sans défense sur le grand méchant Internet. Est-ce de la responsabilité à Google de protéger l'utilisateur (dans ce cas particulier) ? Non. Actuellement la sécurité informatique est un gag, un énorme gag même, pour la simple est bonne raison que les responsables s'en tapent purement et simplement : ceux qui produisent des logiciels pourris (en terme de sécurité).</p>
<p>En effet ces sociétés devraient être tenue pour responsable. Microsoft ou Apple (ou d'autres comme Sony) font des milliards en vendant des produits défectueux. On va, bien entendu, me dire que ce n'est pas possible de faire un programme 100% sécurisé, mais là n'est pas la question. <a href="http://en.wikipedia.org/wiki/Stuxnet">Un virus informatique</a> peut endommager des machines utilisées dans l'industrie nucléaire. Ce n'est pas nouveau : le problème du <a href="http://en.wikipedia.org/wiki/Therac-25">Therac-25</a> qui irradia (et tua) au moins 6 personnes dans les années 80 notamment à cause d'un bug logiciel.</p>
<p>Ce que je souhaite réellement, c'est qu'enfin des garanties soient apportées par les vendeurs de logiciels. Mais pas si on prend un contrat spécial support, dès l'achat du logiciel. Pas une garantie "j'ai perdu mes données en voulant faire du rangement avec le bouton <strong>del</strong>" mais une garantie "j'ai été sur le Web et maintenant mon compte en banque est vide parce qu'un méchant virus dans un vilain PDF a pris le contrôle de mon ordinateur"; ces garanties qu'on trouve dans certains logiciels Libres, comme <a href="http://cr.yp.to/djbdns/guarantee.html">djdns</a> ou <a href="http://www.dovecot.org/security.html">dovecot</a>.</p>
<p>Il est temps de suivre (le grand) <a href="http://www.schneier.com/blog/archives/2004/11/computer_securi.html">Bruce Schneier</a> et de mettre les producteurs de code responsable de leurs bugs :</p>
<blockquote>
<p>One hundred percent of the liability shouldn't fall on the shoulders of the software vendor, just as 100% shouldn't fall on the attacker or the network owner. But today, 100% of the cost falls directly on the network owner, and that just has to stop.</p>
</blockquote>
<p><em>Cent pour cent de la responsabilité ne devrait pas être mise sur le créateur de logiciel, autant que le 100% ne devrait pas être sur l'attaquant ou le propriétaire du réseau. Mais aujourd'hui, 100% des coûts retombent sur le propriétaire du réseau, et ça, ça ne devrait plus exister.</em></p>
<p>Oui aujourd'hui c'est l'administrateur qui s'occupe entièrement de la sécurité du réseau et des logiciels. C'est toi, le pauvre admin, qui va faire le tour de toutes les machines parce qu'une infection a été détectée. Et si des donnée sont perdues à cause de ça, c'est toi qu'on va insulter parce que tu n'as pas su sécurisé ces bouses de Flash ou autres Windows. Et en même temps on voit Apple indiquer à son support de ne pas <a href="http://nakedsecurity.sophos.com/2011/05/18/malware-on-your-mac-dont-expect-applecare-to-help-you-remove-it/">aider les victimes d'un virus pour le supprimer</a>, déplaçant une fois de plus le problème sur l'administrateur.</p>
<p>J'ai souvenir d'un achat d'un appareil d'analyse. La machine était livrée avec un ordinateur. Afin de gagner du temps, ils ont pris l'ordinateur de démo, celui qui avait fait déjà une trentaine de labos. Et ils sont arrivés avec un ordinateur chargé de virus et des données d'autres labos (dans le domaine de la génétique, j'ai pas regarder avant d'atomiser la machine, mais il y'avait certainement de l'ADN humain, vive la protection de la vie privé). Et c'est qui qui nettoie le bordel ? l'administrateur ...</p>
<p>Pourquoi j'ai parlé de Google et de censure au début ? C'est le même genre de cas. Google, dans cette situation, est l'administrateur. Et c'est pas à lui de faire de la censure de résultats pour protéger des logiciels mal-foutus. Ce n'est pas le plus efficace (les sites existent toujours, ils sont plus dur à trouver), ça emmerde ceux qui ont aussi utilisés, honnêtement, un sous-domaine co.cc et ça rend le moteur de recherche moins neutre. </p>
<p>Responsabiliser les producteurs de logiciel et/ou le permis de tuer pour les administrateurs, c'est ainsi qu'on améliorera la sécurité globale. Bon, je vais aller exécuter l'utilisateur qui a amené un virus la semaine passée (selon les rites sacrés des administrateurs Mayas).</p><div><a href="https://linuxfr.org/users/tchetch/journaux/google-censure.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/86758/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/tchetch/journaux/google-censure#comments">ouvrir dans le navigateur</a>
</p>
Etienne Bagnoudhttps://linuxfr.org/nodes/86758/comments.atomtag:linuxfr.org,2005:Diary/310272011-04-21T10:24:48+02:002011-04-21T10:24:48+02:00Social Engineering, un nouvel outil sur le marché.Licence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<p>Pour ceux qui s'intéresse, de prêt ou de loin, à l'ingénierie sociale auront très vite compris que l'obtention de données est un élément clé. Plus vous connaissez d'informations sur la victime, plus vous aurez de possibilités d'attaque.</p>
<p>Imaginons, par exemple, que vous voulez vous introduire dans le bureau du boss d'une entreprise afin d'installer un programme malicieux sur son ordinateur. Une solution est de débarquer un jour où il n'est pas là et convaincre sa secrétaire de vous laissez entrer dans le bureau. Afin de pouvoir le faire, il faut réussir à établir une confiance avec celle-ci et une introduction comme :</p>
<blockquote><p>Bonjour, j'ai rencontré M. Smith lors de ses vacances de février à Vienne [...]</p></blockquote>
<p>Est un bon début. Il est très probable que la secrétaire sache ceci et le fait que vous le saviez permet d'indiquer une certaine intimité entre vous et M. Smith.</p>
<p>C'est pourquoi la firme de Cupertino nous offre une nouvelle révolution <a href="http://www.guardian.co.uk/technology/2011/apr/20/iphone-tracking-prompts-privacy-fears">en traquant les positions GPS des d'iPhone</a> et sachant qu'il a longtemps <a href="http://www.jailbreakme.com/faq.html">été possible de jailbreaké ces machines par simple visite d'une page web</a>, il est évident que cela lève des questions quand à la sécurité des utilisateurs d'iPhone.</p>
<p>Bien entendu, un mari soupçonneux peut très facilement suivre sa femme (ou l'inverse, bien entendu) en lui offrant un joli iPhone 4G : celle-ci sera comblée de bonheur d'être en permanence suivie :D <br />
Bref, méfiez-vous si un inconnu ou votre copine vous offre un iPhone, brûlez-le (le iPhone).</p><div><a href="https://linuxfr.org/users/tchetch/journaux/social-engineering-un-nouvel-outil-sur-le-march%C3%A9.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/85734/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/tchetch/journaux/social-engineering-un-nouvel-outil-sur-le-march%C3%A9#comments">ouvrir dans le navigateur</a>
</p>
Etienne Bagnoudhttps://linuxfr.org/nodes/85734/comments.atomtag:linuxfr.org,2005:Diary/309692011-04-06T18:18:53+02:002011-04-06T18:18:53+02:00SSL, et l'escroquerie continue Licence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<p>J'ai déjà parlé de <a href="https://linuxfr.org/users/tchetch/journaux/ssl">ce sujet ici</a>, je vais donc faire très court.</p>
<p>Dans le journal précédent j'avais déjà parlé des certificats signé pour "localhost" ou autre nom non <a href="http://fr.wikipedia.org/wiki/Fully_qualified_domain_name">FQDN</a>. Et bien voilà que le groupe de <a href="https://www.eff.org/observatory">l'observatoire SSL de l'EFF</a> arrive avec des nouvelles données intéressantes mais effrayantes. <br />
Nos amis, les <a href="http://fr.wikipedia.org/wiki/Certificate_Authority">CA</a>, ont tout simplement signé <a href="https://threatpost.com/en_us/blogs/problem-issuing-certs-unqualified-names-040611">plus de 37'000 certificats SSL valides ayant des noms bidons</a>. Par exemple, ils ont trouvé <a href="https://www.eff.org/deeplinks/2011/04/unqualified-names-ssl-observatory">806 certificats ayant comme nom "exchange"</a>. <br />
À l'aide des données de l'observatoire, d'autres informations ont pu être trouvées, comme <a href="http://www.prism.gatech.edu/~gmacon3/ssl-observatory/unqualified_local_rfc1918_ev.txt">les 10 certificats EVP</a> signant des noms pas réglementaires.</p>
<p>Encore d'autres surprises nous attendent certainement dans les joies du SSL et son modèle du <a href="http://fr.wikipedia.org/wiki/Tiers_de_confiance">tiers de confiance</a>.</p><div><a href="https://linuxfr.org/users/tchetch/journaux/ssl-et-lescroquerie-continue.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/85545/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/tchetch/journaux/ssl-et-lescroquerie-continue#comments">ouvrir dans le navigateur</a>
</p>
Etienne Bagnoudhttps://linuxfr.org/nodes/85545/comments.atom