philpep's blog - tag C philpep's blog - tag C rss http://blog.philpep.org fr Tue, 20 Apr 2010 18:38:22 GMT Pblog Le parseur de wmfs http://blog.philpep.org/post/Le-parseur-de-wmfs http://blog.philpep.org/post/Le-parseur-de-wmfs Tue, 20 Apr 2010 18:38:22 GMT <p><strong>EDIT</strong>: ce billet se voulait une vulgarisation, mais il faut quand même savoir manipuler les notions d'automates, de grammaire etc. Si ça donne envie d'en savoir plus, tant mieux.</p> <p>En même temps que j'ai laché <a href="http://dwm.suckless.org">dwm</a> pour revenir au bon vieux (pas si vieux que ça) <a href="http://www.wmfs.info">wmfs</a>, j'ai totalement recodé le parseur du fichier de configuration.</p> <p>Un fichier de configuration se doit d'être lisible, et sa projection en mémoire doit être facile à manipuler, malheureusement utiliser le même langage (comme le fait dwm avec son fichier de configuration directement en C) rend la configuration moins convi pour peu que l'on ne soit pas expert. Le parseur sert à ça, traduire un langage simple en langage compréhensible par la machine.</p> <p>L'idée c'est d'expliquer un peu comment le parseur de wmfs fonctionne parce que ça touche une partie de l'informatique théorique que j'aime bien : l'étude des <a href="http://fr.wikipedia.org/wiki/Langage_formel">langages formels</a>.</p> <p><a href="http://fr.wikipedia.org/wiki/Lex_et_yacc">Yacc et lex</a> sont souvent utilisés pour ce genre de choses. Mon code est juste une implémentation C d'un <a href="http://fr.wikipedia.org/wiki/Machine_%C3%A0_%C3%A9tats_finis">automate fini déterministe</a>, il ne doit pas être loin du fonctionnement de yacc <strong>pour une grammaire et une syntaxe particulière</strong> (évidement yacc&amp;lex sont beaucoup plus évolués que mon petit bout de code).</p> <p>Donc le travail de lex c'est de lire le fichier caractère par caractère et de renvoyer des <a href="http://fr.wikipedia.org/wiki/Analyse_lexicale#Token">token</a>, c'est à dire un identifiant syntaxique.</p> <p>Le langage de configuration est comme ça :</p> <div class="codehilite"><pre>[section] option = valeur truc = { &quot;liste&quot;, 1, False } [sous-section] [sous-sous-section] machin = True [/sous-sous-section] [/sous-section] [/section] </pre></div> <p>Je fais une première transformation purement syntaxique :</p> <div class="codehilite"><pre><span class="k">enum</span> <span class="n">conf_type</span> <span class="p">{</span> <span class="n">SEC_START</span><span class="p">,</span> <span class="n">SEC_END</span><span class="p">,</span> <span class="n">WORD</span><span class="p">,</span> <span class="n">EQUAL</span><span class="p">,</span> <span class="n">LIST_START</span><span class="p">,</span> <span class="n">LIST_END</span><span class="p">,</span> <span class="n">NONE</span> <span class="p">};</span> <span class="cm">/* qui correspond à [ [/ un_mot = { } autre */</span> </pre></div> <p>Et je range ça dans deux listes chainées, une qui contient des conf_type et une autre qui contient les mots (des char *) :</p> <div class="codehilite"><pre>Liste des TOKEN Liste des mots (WORD) SEC_START WORD &quot;section&quot; WORD &quot;option&quot; EQUAL WORD &quot;valeur&quot; WORD &quot;truc&quot; EQUAL LIST_START WORD &quot;liste&quot; WORD &quot;1&quot; WORD &quot;False&quot; SEC_START WORD &quot;sous-section&quot; SEC_START WORD &quot;sous-sous-section&quot; WORD &quot;machin&quot; EQUAL WORD &quot;True&quot; SEC_END WORD &quot;sous-sous-section&quot; SEC_END WORD &quot;sous-section&quot; SEC_END WORD &quot;section&quot; </pre></div> <p>Une fois ces deux piles construites, il suffit de dépiler en suivant la grammaire (c'est ce que fait yacc). C'est là qu'on parle d'états finis. Désolé ça aurait été plus compréhensible avec un schéma, mais je sais pas en faire donc j'ai écrit ça avec la syntaxe qu'on utilise pour décrire des grammaires.</p> <div class="codehilite"><pre>start: /* rien */ | SEC_START WORD sections SEC_END WORD start /* une suite de sections */ ; sections: /* rien */ | SEC_START WORD sections SEC_END WORD /* des sous sections */ | WORD EQUAL option sections /* des options */ ; option: WORD /* un mot tout simple (une valeur quoi) */ | LIST_START list LIST_END /* une liste de valeurs */ ; list: /* rien */ | WORD list /* une liste de mots */ ; </pre></div> <p>Le parseur va ainsi remplir une structure générique capable de contenir toutes les configurations qui respectent la grammaire et la syntaxe.</p> <p>Pour wmfs en gros c'est ça :</p> <div class="codehilite"><pre><span class="k">struct</span> <span class="n">conf_opt</span> <span class="p">{</span> <span class="cm">/* options */</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">;</span> <span class="kt">char</span> <span class="o">*</span><span class="n">val</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span> <span class="cm">/* au plus 10 éléments dans une liste */</span> <span class="n">SLIST_ENTRY</span><span class="p">(</span><span class="n">conf_opt</span><span class="p">)</span> <span class="n">entry</span><span class="p">;</span> <span class="p">}</span> <span class="k">struct</span> <span class="n">conf_sec</span> <span class="p">{</span> <span class="cm">/* section */</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">;</span> <span class="n">SLIST_HEAD</span><span class="p">(,</span> <span class="n">conf_opt</span><span class="p">)</span> <span class="n">optlist</span><span class="p">;</span> <span class="cm">/* liste chaînée des options de la section */</span> <span class="n">TAILQ_HEAD</span><span class="p">(,</span> <span class="n">conf_sec</span><span class="p">)</span> <span class="n">sub</span><span class="p">;</span> <span class="cm">/* liste chaînée des sous sections */</span> <span class="n">TAILQ_ENTRY</span><span class="p">(</span><span class="n">conf_sec</span><span class="p">)</span> <span class="n">entry</span><span class="p">;</span> <span class="p">}</span> </pre></div> <p>J'utilise abusivement des macros de <a href="http://www.freebsd.org/cgi/man.cgi?query=queue">&lt;sys/queue.h&gt;</a>.</p> <p>Ensuite il suffit de coder une API qui va récupérer des options/sections précises sur la structure, mais c'est pas le plus dur à faire.</p> <p>Voilà un peu l'idée, l'intérêt de cette méthode c'est qu'elle est certaine (pas ou peu de bugs incompréhensibles), rapide : le fichier de configuration n'est traversé qu'une seule fois, pas besoin de strlen(), strchr(), strcmp(), strstr() qui prennent des plombes.</p> <p>EDIT : j'ai failli oublier de donner le lien vers <a href="http://git.wmfs.info/wmfs.git/tree/src/parse">le code</a></p> realpath secure ? http://blog.philpep.org/post/realpath-secure http://blog.philpep.org/post/realpath-secure Mon, 15 Mar 2010 00:00:00 GMT <p>Dans le cadre d'une application serveur (type transfert de fichier : http, ftp, ...), pour contrôler l'accès aux fichiers j'utilise <a href="http://www.freebsd.org/cgi/man.cgi?query=realpath&amp;sektion=3">realpath(3)</a>. Cette fonction permet d'avoir le path (chemin) réel d'un fichier, sans lien relatif dans un des dossiers. Ainsi on a juste à faire un <a href="http://www.freebsd.org/cgi/man.cgi?query=strncmp">strncmp(3)</a> pour le comparer à la <em>racine</em> de l'application.</p> <p>Par exemple une partie du code du serveur HTTP que je suis en train de coder (<code>uri</code> est quelque chose de la forme <code>/../foo/bar/../machin.html</code> et <code>conf_root</code> la racine du style <code>/usr/local/www/</code></p> <div class="codehilite"><pre><span class="kt">char</span> <span class="n">path</span><span class="p">[</span><span class="n">PATH_MAX</span><span class="p">];</span> <span class="kt">char</span> <span class="n">root</span><span class="p">[</span><span class="n">PATH_MAX</span><span class="p">];</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">realpath</span><span class="p">(</span><span class="n">uri</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">path</span><span class="p">)</span> <span class="o">||</span> <span class="o">!</span><span class="n">realpath</span><span class="p">(</span><span class="n">conf_root</span><span class="p">,</span> <span class="n">root</span><span class="p">)</span> <span class="o">||</span> <span class="n">strncmp</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">root</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">root</span><span class="p">))</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="n">send_error</span><span class="p">(</span><span class="mi">404</span><span class="p">);</span> </pre></div> <p>Prenez ce post pour une question, à part le <a href="http://www.freebsd.org/cgi/man.cgi?query=chroot&amp;sektion=2">chroot(2)</a> et <em>realpath</em>, comment bien contrôler l'accès aux fichiers ?</p> <p>J'y connais rien en <em>filesystems</em>, mais si on pouvait avoir un moyen rapide de savoir si un fichier est dans tel ou tel fichier ce serait grandement pratique. Sans me vanter j'ai souvent des idées géniales en informatique, mais bien souvent ça a été pensé et implémenté depuis plus de 10 ans ! Du coup si vous avez plus d'information, je suis prêt à converser sur ce sujet avec vous...</p> Jouons avec kvm http://blog.philpep.org/post/Jouons-avec-kvm http://blog.philpep.org/post/Jouons-avec-kvm Sat, 13 Feb 2010 02:31:41 GMT <p>J'ai codé un petit programme tout simple qui fait clignoter les leds de mon routeur dès qu'un service est par terre. Ma méthode (qui ne doit pas être la meilleure mais je m'en fout un peu) consiste à regarder régulièrement la liste des processus et détecter l'arrêt d'un service quand le processus associé n'existe plus. J'ai commencé avec un script shell, puis étant assez mauvais pour tout ce qui ressemble de près ou de loin à du script j'ai décidé de le faire en C (langage d'excellence n'est ce pas...?).</p> <p>Je me suis volontairement compliqué la tache en utilisant des fonctions très proches du système, ainsi s'il est possible d'obtenir une liste de processus via <a href="http://www.freebsd.org/cgi/man.cgi?query=ps">ps(1)</a> avec un <a href="http://www.freebsd.org/cgi/man.cgi?query=popen">popen(3)</a> super crade ou encore <a href="http://www.freebsd.org/cgi/man.cgi?query=sysctl&amp;sektion=3">sysctl(3)</a> j'ai voulu utiliser <a href="http://www.freebsd.org/cgi/man.cgi?query=kvm">kvm(3)</a> <code>kernel memory interface</code> qui est en programmation système la méthode la plus appropriée (d'ailleurs 'ps' est codé avec ça), c'est juste une interface qui va nous permettre d'accéder au données du kernel (une copie bien entendu), ici la liste des processus en cours.</p> <p>Le hic c'est que si les fonctions de kvm sont très standard l'implémentation est très différente suivant les systèmes, comme à peu près toute implémentation, l'important c'est que l'interface soit standard. Ainsi à la lecture de la structure qui décrit les processus <code>kinfo_proc</code> de <a href="http://svn.freebsd.org/viewvc/base/head/sys/sys/user.h?view=markup">sys/user.h</a> on se dit chouette pleins d'info à disposition, sauf que sur OpenBSD c'est pas du tout la même forme (ni le même fichier d'ailleurs). D'où l'importance d'une interface riche et c'est ce que nous promet <code>kvm</code>.</p> <p>Assez causé passons au code (je n'explique pas ou peu le code en dehors des fonctions kvm)</p> <div class="codehilite"><pre><span class="cp">#include &lt;stdio.h&gt;</span> <span class="cp">#include &lt;stdlib.h&gt;</span> <span class="cp">#include &lt;string.h&gt;</span> <span class="cp">#include &lt;unistd.h&gt;</span> <span class="cp">#include &lt;err.h&gt;</span> <span class="cp">#include &lt;libgen.h&gt;</span> <span class="cp">#include &lt;fcntl.h&gt;</span> <span class="cp">#include &lt;limits.h&gt;</span> <span class="cp">#include &lt;paths.h&gt;</span> <span class="cp">#include &lt;kvm.h&gt;</span> <span class="cp">#include &lt;sys/param.h&gt;</span> <span class="cp">#include &lt;sys/sysctl.h&gt;</span> <span class="cp">#include &lt;sys/wait.h&gt;</span> <span class="cp">#include &lt;sys/user.h&gt;</span> <span class="kt">void</span> <span class="nf">start_leds</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* Ici on execute /usr/sbin/gioctl -q gpio0 led3 (on|off)</span> <span class="cm"> * suivant la parité du conteur statique &#39;i&#39;</span> <span class="cm"> * 60 fois avec une pause d&#39;une seconde. */</span> <span class="k">static</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="k">switch</span><span class="p">(</span><span class="n">fork</span><span class="p">())</span> <span class="p">{</span> <span class="k">case</span> <span class="o">-</span><span class="mi">1</span>: <span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">&quot;fork&quot;</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> <span class="k">case</span> <span class="mi">0</span>: <span class="n">execl</span><span class="p">(</span><span class="s">&quot;/usr/sbin/gpioctl&quot;</span><span class="p">,</span> <span class="s">&quot;gpioctl&quot;</span><span class="p">,</span> <span class="s">&quot;-q&quot;</span><span class="p">,</span> <span class="s">&quot;gpio0&quot;</span><span class="p">,</span> <span class="s">&quot;led3&quot;</span><span class="p">,</span> <span class="p">(</span><span class="n">i</span><span class="o">%</span><span class="mi">2</span><span class="p">)</span> <span class="o">?</span> <span class="s">&quot;on&quot;</span> <span class="o">:</span> <span class="s">&quot;off&quot;</span><span class="p">,</span> <span class="p">(</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="nb">NULL</span><span class="p">);</span> <span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">&quot;execl&quot;</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> <span class="nl">default:</span> <span class="n">wait</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> <span class="p">}</span> <span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">i</span><span class="o">++</span> <span class="o">&lt;=</span> <span class="mi">60</span><span class="p">)</span> <span class="k">return</span> <span class="n">start_leds</span><span class="p">();</span> <span class="k">return</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">kvm_t</span> <span class="o">*</span><span class="n">kd</span><span class="p">;</span> <span class="kt">char</span> <span class="n">berr</span><span class="p">[</span><span class="n">_POSIX2_LINE_MAX</span><span class="p">];</span> <span class="k">struct</span> <span class="n">kinfo_proc</span> <span class="o">*</span><span class="n">p</span><span class="p">,</span> <span class="o">*</span><span class="n">procs</span><span class="p">;</span> <span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="n">j</span><span class="p">,</span> <span class="n">k</span><span class="p">,</span> <span class="n">found</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">pargv</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="n">argc</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">)</span> <span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">&quot;Usage check_proc procs ...&quot;</span><span class="p">);</span> <span class="cm">/* On accède aux données du kernel qui tourne actuellement (NULL) */</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">kd</span> <span class="o">=</span> <span class="n">kvm_open</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">O_RDONLY</span><span class="p">,</span> <span class="n">berr</span><span class="p">)))</span> <span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">&quot;%s&quot;</span><span class="p">,</span> <span class="n">berr</span><span class="p">);</span> <span class="cm">/* On demande la liste des processus, kinfo_proc est décrite</span> <span class="cm"> * dans sys/sysctl.h sur OpenBSD et sys/user.h sur FreeBSD.</span> <span class="cm"> * Il nous la donne dans un tableau continu de pointeurs de</span> <span class="cm"> * taille qu&#39;on récupère dans &#39;n&#39;. */</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">procs</span> <span class="o">=</span> <span class="n">kvm_getprocs</span><span class="p">(</span><span class="n">kd</span><span class="p">,</span> <span class="n">KERN_PROC_ALL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">n</span><span class="p">)))</span> <span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">&quot;kvm_getprocs: %s&quot;</span><span class="p">,</span> <span class="n">kvm_geterr</span><span class="p">(</span><span class="n">kd</span><span class="p">));</span> <span class="k">for</span> <span class="p">(</span><span class="n">k</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">k</span> <span class="o">&lt;</span> <span class="n">argc</span><span class="p">;</span> <span class="n">k</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="k">for</span> <span class="p">(</span><span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">p</span> <span class="o">=</span> <span class="n">procs</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">,</span> <span class="n">p</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* On accède à la liste des arguments du programme qui a</span> <span class="cm"> * généré le processus courant. */</span> <span class="n">pargv</span> <span class="o">=</span> <span class="n">kvm_getargv</span><span class="p">(</span><span class="n">kd</span><span class="p">,</span> <span class="n">p</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">pargv</span> <span class="o">&amp;&amp;</span> <span class="n">pargv</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">strstr</span><span class="p">(</span><span class="n">pargv</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">argv</span><span class="p">[</span><span class="n">k</span><span class="p">]))</span> <span class="p">{</span> <span class="n">found</span><span class="o">++</span><span class="p">;</span> <span class="k">break</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="k">if</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span> <span class="o">==</span> <span class="n">kvm_close</span><span class="p">(</span><span class="n">kd</span><span class="p">))</span> <span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">&quot;kvm_close&quot;</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">found</span> <span class="o">!=</span> <span class="n">argc</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">start_leds</span><span class="p">();</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> </pre></div> <p>Et l'exécution, on veut tester si un processus du nom de 'dhcpd' tourne sur le système, si ce n'est pas le cas on lance le clignotement de la led pendant une minute :</p> <div class="codehilite"><pre># gcc -o check_proc check_proc.c -lkvm # ./check_proc dhcpd </pre></div> <p>Le mien s'exécute dans un cron toute les minutes :</p> <div class="codehilite"><pre>* * * * * /root/bin/check_proc named dhcpd adsuck ntpd </pre></div> Un système de ramasse miette en C http://blog.philpep.org/post/Un-système-de-ramasse-miette-en-C http://blog.philpep.org/post/Un-système-de-ramasse-miette-en-C Sat, 03 Oct 2009 00:00:00 GMT <p>Je viens d'implémenter dans <a href="http://git.philpep.org/lsh.git">mon shell</a> un système de <a href="http://fr.wikipedia.org/wiki/Ramasse-miettes_%28informatique%29">ramasse miette</a>. Le choix de faire un tel système est particulièrement adapté pour un shell ou pour tout programme qui est fortement cyclique et avec un cycle assez court où chaque allocation peut être libérée rapidement.</p> <p>En gros l'astuce consiste à créer des fonctions wrapper pour <a href="http://www.freebsd.org/cgi/man.cgi?query=free">free(3)</a> et <a href="http://www.freebsd.og/cgi/man.cgi?query=malloc">malloc(3)</a>. Une fois que tous nos appels d'allocation et de libération de mémoire passent par eux il est facile de créer une structure de type <a href="http://fr.wikipedia.org/wiki/Pile_%28informatique%29">pile</a> qui empile l'adresse de chaque allocation pour ensuite tout libérer d'un coup à la fin du cycle.</p> <p>Pour cela j'ai utilisé les <a href="http://blog.philpep.org/post/Petite-pr%C3%A9sentation-de-sys/queue.h">SLIST</a> de sys/queue.h pour la pile que voilà :</p> <div class="codehilite"><pre><span class="k">typedef</span> <span class="k">struct</span> <span class="n">stack_s</span> <span class="p">{</span> <span class="kt">void</span> <span class="o">*</span> <span class="n">adr</span><span class="p">;</span> <span class="n">SLIST_ENTRY</span><span class="p">(</span><span class="n">stack_s</span><span class="p">)</span> <span class="n">next</span><span class="p">;</span> <span class="p">}</span> <span class="n">stack_s</span><span class="p">;</span> </pre></div> <p>Ensuite le wrapper pour malloc (notez qu'on déclare la tête de pile comme variable globale (dans le fichier avec les fonctions wrapper) pour pouvoir y accéder facilement.</p> <div class="codehilite"><pre><span class="cm">/* déclaration de la pile mstack (qu&#39;on initialise à NULL) */</span> <span class="k">static</span> <span class="n">SLIST_HEAD</span><span class="p">(,</span> <span class="n">stack_s</span><span class="p">)</span> <span class="n">mstack</span> <span class="o">=</span> <span class="n">SLIST_HEAD_INITIALIZER</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mstack</span><span class="p">);</span> <span class="cm">/* Le wrapper pour malloc */</span> <span class="kt">void</span> <span class="o">*</span> <span class="nf">xmalloc</span><span class="p">(</span><span class="kt">size_t</span> <span class="n">size</span><span class="p">)</span> <span class="p">{</span> <span class="kt">void</span> <span class="o">*</span><span class="n">ret</span><span class="p">;</span> <span class="n">stack_s</span> <span class="o">*</span><span class="n">el</span><span class="p">;</span> <span class="cm">/* l&#39;élément a rajouter dans la pile */</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">size</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">ret</span><span class="p">)</span> <span class="p">{</span> <span class="n">perror</span><span class="p">(</span><span class="s">&quot;Malloc &quot;</span><span class="p">);</span> <span class="n">exit</span> <span class="p">(</span><span class="n">EXIT_FAILURE</span><span class="p">);</span> <span class="p">}</span> <span class="n">el</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">el</span><span class="p">));</span> <span class="n">el</span><span class="o">-&gt;</span><span class="n">adr</span> <span class="o">=</span> <span class="n">ret</span><span class="p">;</span> <span class="n">SLIST_INSERT_HEAD</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mstack</span><span class="p">,</span> <span class="n">el</span><span class="p">,</span> <span class="n">next</span><span class="p">);</span> <span class="k">return</span> <span class="n">ret</span><span class="p">;</span> <span class="p">}</span> <span class="cm">/* Cette fonction vide toute la pile et libère la mémoire */</span> <span class="kt">void</span> <span class="nf">stack_delete</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> <span class="n">stack_s</span> <span class="o">*</span><span class="n">el</span><span class="p">;</span> <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">SLIST_EMPTY</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mstack</span><span class="p">))</span> <span class="p">{</span> <span class="n">el</span> <span class="o">=</span> <span class="n">SLIST_FIRST</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mstack</span><span class="p">);</span> <span class="n">SLIST_REMOVE_HEAD</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mstack</span><span class="p">,</span> <span class="n">next</span><span class="p">);</span> <span class="n">free</span><span class="p">(</span><span class="n">el</span><span class="o">-&gt;</span><span class="n">adr</span><span class="p">);</span> <span class="n">free</span><span class="p">(</span><span class="n">el</span><span class="p">);</span> <span class="p">}</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span> </pre></div> <p>Voilà, maintenant faites tous vos allocations temporaires avec _malloc et videz la pile de temps en temps (ie à la fin du cycle général de votre programme).</p> <p>Par exemple dans la boucle principale ça donne quelque chose comme ça :</p> <div class="codehilite"><pre><span class="k">for</span> <span class="p">(;;)</span> <span class="p">{</span> <span class="n">machin</span> <span class="o">=</span> <span class="n">xmalloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">machin</span><span class="p">)</span> <span class="o">*</span> <span class="mi">10</span><span class="p">);</span> <span class="cm">/*</span> <span class="cm"> ...</span> <span class="cm"> ...</span> <span class="cm"> */</span> <span class="n">stack_delete</span><span class="p">();</span> <span class="p">}</span> </pre></div> <p>J'espère que c'est compréhensible, si vous avez une question n'hésitez surtout pas à me demander plus d'information.</p> Petite présentation de sys/queue.h http://blog.philpep.org/post/Petite-présentation-de-sys/queue.h http://blog.philpep.org/post/Petite-présentation-de-sys/queue.h Sat, 26 Sep 2009 22:27:29 GMT <p>Dans vos programmes C, il arrive très fréquemment d'utiliser des listes chaînée (de toutes sortes), afin de ne pas avoir à recréer le monde à chaque fois vous pouvez utiliser les macros super pratiques de <a href="http://www.freebsd.org/cgi/man.cgi?query=queue">queue(3)</a>. Voici une petite introduction à l'utilisation de sys/queue.h avant d'aller se plonger dans le man pour aller plus loin.</p> <p>Avec sys/queue.h , vous pouvez manipuler facilement quatres sortes de listes :</p> <ul> <li>Listes simplement chainées (SLIST) de type pile</li> <li>Listes doublement chainées (LIST) de type pile</li> <li>Listes simplement chainées (STAILQ) de type file</li> <li>Listes doublement chainées (TAILQ) de type file</li> </ul> <p>Voici un exemple de SLIST :</p> <p>Le header :</p> <div class="codehilite"><pre><span class="cp">#ifndef HEADER_H</span> <span class="cp">#define HEADER_H</span> <span class="cp">#include &lt;sys/queue.h&gt;</span> <span class="k">typedef</span> <span class="k">struct</span> <span class="n">MaStruct</span> <span class="p">{</span> <span class="cm">/* ici on met nos elements par exemple : */</span> <span class="kt">char</span> <span class="o">*</span><span class="n">str</span><span class="p">;</span> <span class="cm">/*</span> <span class="cm"> * On déclare notre liste avec SLIST_ENTRY qui est equivalente à</span> <span class="cm"> * struct { struct MaStruct *sle_next; } next;</span> <span class="cm"> */</span> <span class="n">SLIST_ENTRY</span><span class="p">(</span><span class="n">MaStruct</span><span class="p">)</span> <span class="n">next</span><span class="p">;</span> <span class="p">}</span> <span class="n">MaStruct</span><span class="p">;</span> <span class="cp">#endif </span><span class="cm">/* HEADER_H */</span><span class="cp"></span> </pre></div> <p>Ensuite voyons comment manipuler cette liste :</p> <div class="codehilite"><pre><span class="cp">#include &lt;stdio.h&gt;</span> <span class="cp">#include &lt;stdlib.h&gt;</span> <span class="cp">#include &lt;string.h&gt;</span> <span class="cp">#include &quot;header.h&quot;</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* SLIST_HEAD crée une variable pour acceder à notre liste */</span> <span class="n">SLIST_HEAD</span><span class="p">(,</span> <span class="n">MaStruct</span><span class="p">)</span> <span class="n">head</span><span class="p">;</span> <span class="cm">/* on crée un element MaStruct */</span> <span class="n">MaStruct</span> <span class="o">*</span><span class="n">p</span><span class="p">;</span> <span class="cm">/* On l&#39;initialise à NULL */</span> <span class="n">SLIST_INIT</span><span class="p">(</span><span class="o">&amp;</span><span class="n">head</span><span class="p">);</span> <span class="n">p</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">p</span><span class="p">));</span> <span class="n">p</span><span class="o">-&gt;</span><span class="n">str</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">char</span><span class="p">)</span> <span class="o">*</span> <span class="mi">10</span><span class="p">);</span> <span class="n">strcpy</span><span class="p">(</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">str</span><span class="p">,</span> <span class="s">&quot;blah&quot;</span><span class="p">);</span> <span class="cm">/* On insere p dans la SLIST */</span> <span class="n">SLIST_INSERT_HEAD</span><span class="p">(</span><span class="o">&amp;</span><span class="n">head</span><span class="p">,</span> <span class="n">p</span><span class="p">,</span> <span class="n">next</span><span class="p">);</span> <span class="cm">/* On empile un deuxième element */</span> <span class="n">p</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">p</span><span class="p">));</span> <span class="n">p</span><span class="o">-&gt;</span><span class="n">str</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">char</span><span class="p">)</span> <span class="o">*</span> <span class="mi">10</span><span class="p">);</span> <span class="n">strcpy</span><span class="p">(</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">str</span><span class="p">,</span> <span class="s">&quot;blu&quot;</span><span class="p">);</span> <span class="n">SLIST_INSERT_HEAD</span><span class="p">(</span><span class="o">&amp;</span><span class="n">head</span><span class="p">,</span> <span class="n">p</span><span class="p">,</span> <span class="n">next</span><span class="p">);</span> <span class="cm">/*</span> <span class="cm"> * Il est très facile de parcourir la liste</span> <span class="cm"> * que ce soit pour du traitement ou de</span> <span class="cm"> * la libération de mémoire</span> <span class="cm"> */</span> <span class="n">SLIST_FOREACH</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">head</span><span class="p">,</span> <span class="n">next</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* ici p est l&#39;element courant */</span> <span class="n">printf</span><span class="p">(</span><span class="s">&quot;element : %s</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">,</span> <span class="n">p</span><span class="o">-&gt;</span><span class="n">str</span><span class="p">);</span> <span class="p">}</span> <span class="cm">/* exemple de libération */</span> <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">SLIST_EMPTY</span><span class="p">(</span><span class="o">&amp;</span><span class="n">head</span><span class="p">))</span> <span class="p">{</span> <span class="n">p</span> <span class="o">=</span> <span class="n">SLIST_FIRST</span><span class="p">(</span><span class="o">&amp;</span><span class="n">head</span><span class="p">);</span> <span class="n">SLIST_REMOVE_HEAD</span><span class="p">(</span><span class="o">&amp;</span><span class="n">head</span><span class="p">);</span> <span class="n">free</span><span class="p">(</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">str</span><span class="p">);</span> <span class="n">free</span><span class="p">(</span><span class="n">p</span><span class="p">);</span> <span class="p">}</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> </pre></div> <p>Parmis les macros interessantes pour les SLIST il y a :</p> <div class="codehilite"><pre><span class="n">SLIST_FIRST</span><span class="p">(</span><span class="o">&amp;</span><span class="n">head</span><span class="p">);</span> <span class="cm">/* renvoie la tête de liste */</span> <span class="n">SLIST_REMOVE_HEAD</span><span class="p">(</span><span class="o">&amp;</span><span class="n">head</span><span class="p">,</span> <span class="n">next</span><span class="p">);</span> <span class="cm">/* supprime la tete de liste */</span> <span class="n">SLIST_EMPTY</span><span class="p">(</span><span class="o">&amp;</span><span class="n">head</span><span class="p">);</span> <span class="cm">/* renvoie vrai si la liste est vide */</span> </pre></div> <p>Ben entendu, il y a beaucoup plus de possibilités avec LIST et TAILQ... Mais globalement vous devriez avoir compris le principe.</p> <p>La lecture de sys/queue.h devrait vous éclairer pour comprendre comment marchent toutes ces macros, c'est assez facile à comprendre si vous avez (comme moi) un niveau moyen en C.</p> <p>Voyez aussi <a href="http://imil.net/docs/slists.txt">ce papier d'iMil</a>.</p> Parser une saisie en C avec la fonction strtok http://blog.philpep.org/post/Parser-une-saisie-en-C-avec-la-fonction-strtok http://blog.philpep.org/post/Parser-une-saisie-en-C-avec-la-fonction-strtok Thu, 10 Sep 2009 21:53:37 GMT <p>Si vous récupérez une saisie de l'utilisateur et que vous souhaitez l'analyser selon une syntaxe particulière pour en retirer des arguments c'est que vous voulez parser la saisie.</p> <p>Une solution simple et portable en C est la fonction strtok de string.h . Par exemple si à partir de la chaine char str = "fonction argument1 argument2" vous voulez obtenir un char **argv = {"fonction", "argument1", argument2"} c'est une bonne idée d'utiliser la fonction strtok.</p> <p>On pourrait croire qu'il y a plus simple, juste en utilisant quelques pointeurs, mais imaginez un peu que l'utilisateur s'amuse a mettre des espaces n'importe comment par exemple " fonction argument1 argument2 " ...</p> <p>Donc voici un code qui permet d'allouer juste comme il faut votre char **argv :</p> <div class="codehilite"><pre><span class="cp">#include &lt;stdio.h&gt;</span> <span class="cp">#include &lt;stdlib.h&gt;</span> <span class="cp">#include &lt;string.h&gt;</span> <span class="cp">#define MAX_ARG 10</span> <span class="cp">#define SIZE 256</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> <span class="kt">char</span> <span class="o">**</span><span class="n">argv</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">saisie</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">char</span><span class="p">)</span> <span class="o">*</span> <span class="n">SIZE</span><span class="p">);</span> <span class="kt">char</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span> <span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="kt">char</span> <span class="n">c</span><span class="p">;</span> <span class="cm">/* On recupère la saisie */</span> <span class="n">printf</span><span class="p">(</span><span class="s">&quot;&gt;&quot;</span><span class="p">);</span> <span class="n">fgets</span><span class="p">(</span><span class="n">saisie</span><span class="p">,</span> <span class="n">SIZE</span><span class="p">,</span> <span class="n">stdin</span><span class="p">);</span> <span class="cm">/* On supprime le de la fin */</span> <span class="k">if</span><span class="p">(</span><span class="nb">NULL</span> <span class="o">!=</span> <span class="p">(</span><span class="n">p</span> <span class="o">=</span> <span class="n">strrchr</span><span class="p">(</span><span class="n">saisie</span><span class="p">,</span> <span class="sc">&#39; &#39;</span><span class="p">)))</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="sc">&#39;\0&#39;</span><span class="p">;</span> <span class="k">else</span> <span class="k">while</span><span class="p">(</span><span class="sc">&#39; &#39;</span> <span class="o">!=</span> <span class="p">(</span><span class="n">c</span> <span class="o">=</span> <span class="n">fgetc</span><span class="p">(</span><span class="n">stdin</span><span class="p">))</span> <span class="o">&amp;&amp;</span> <span class="n">c</span> <span class="o">!=</span> <span class="n">EOF</span><span class="p">);</span> <span class="cm">/* On alloue argv */</span> <span class="n">argv</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span> <span class="o">*</span> <span class="n">MAX_ARG</span><span class="p">);</span> <span class="cm">/* </span> <span class="cm"> * On lance le premier strtok</span> <span class="cm"> * char *strtok(char *ptr, char *str);</span> <span class="cm"> * ptr doit être la saisie qu&#39;on veut parser</span> <span class="cm"> * et str est une chaine qui contient les caractères</span> <span class="cm"> * sur lesquels on va couper la saisie. Vous pouvez</span> <span class="cm"> * bien sûr en mettre plusieurs</span> <span class="cm"> */</span> <span class="n">p</span> <span class="o">=</span> <span class="n">strtok</span><span class="p">(</span><span class="n">saisie</span><span class="p">,</span> <span class="s">&quot; &quot;</span><span class="p">);</span> <span class="k">while</span><span class="p">(</span><span class="n">p</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* </span> <span class="cm"> * Ici p est un pointeur sur une chaine</span> <span class="cm"> * qui contient exactement l&#39;argument i</span> <span class="cm"> */</span> <span class="k">if</span><span class="p">(</span><span class="n">i</span> <span class="o">&lt;</span> <span class="n">MAX_ARG</span><span class="p">)</span> <span class="p">{</span> <span class="n">argv</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">char</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span><span class="o">+</span><span class="n">strlen</span><span class="p">(</span><span class="n">p</span><span class="p">)));</span> <span class="n">strcpy</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">p</span><span class="p">);</span> <span class="n">i</span><span class="o">++</span><span class="p">;</span> <span class="p">}</span> <span class="k">else</span> <span class="k">break</span><span class="p">;</span> <span class="cm">/* </span> <span class="cm"> * On lance un nouvel appel a strtok</span> <span class="cm"> * par contre on lui donne en argument NULL</span> <span class="cm"> * pour qu&#39;il sache que c&#39;est celle du dernier</span> <span class="cm"> * appel, on peut aussi changer les caractères</span> <span class="cm"> * pour parser...</span> <span class="cm"> */</span> <span class="n">p</span> <span class="o">=</span> <span class="n">strtok</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="s">&quot; &quot;</span><span class="p">);</span> <span class="p">}</span> <span class="n">argv</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span> <span class="cm">/* On affiche les resultats */</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">argv</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">!=</span> <span class="nb">NULL</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">printf</span><span class="p">(</span><span class="s">&quot;arg[%d] = &#39;%s&#39; &quot;</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">argv</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span> <span class="n">free</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span> <span class="p">}</span> <span class="n">free</span><span class="p">(</span><span class="n">argv</span><span class="p">);</span> <span class="n">free</span><span class="p">(</span><span class="n">saisie</span><span class="p">);</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> </pre></div> <p>Attention : la chaine saisie a été modifiée par strtok, elle ne doit pas avoir l'attribut const pour ça. D'ailleurs si vous envoyez une chaine constante à strtok ça fait un segfault...</p> <p>Bon, le seul problème avec cette technique c'est que strtok ne nous dit pas sur quel caractère il a coupé la chaine. Mais ça reste un moyen rapide d'arriver à faire quelque chose de propre...</p> <p>Bons codes :-)</p> <p>EDIT : Il manquait quelques free...</p> Lire /etc/passwd pour avoir des informations en C http://blog.philpep.org/post/Lire-/etc/passwd-pour-avoir-des-informations-en-C http://blog.philpep.org/post/Lire-/etc/passwd-pour-avoir-des-informations-en-C Thu, 10 Sep 2009 00:00:00 GMT <p>Dans vos programmes C, il peut arriver d'avoir besoin d'informations concernant l'utilisateur du programme (Son $HOME pour y enregistrer des fichier par exemple).</p> <p>Une manière simple (et portable) de procéder est d'utiliser les variables d'environnement. Par exemple : getenv.c</p> <div class="codehilite"><pre><span class="cp">#include &lt;stdio.h&gt;</span> <span class="cp">#include &lt;stdlib.h&gt;</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> <span class="n">printf</span><span class="p">(</span><span class="s">&quot;login: %s &quot;</span><span class="p">,</span> <span class="n">getenv</span><span class="p">(</span><span class="s">&quot;USER&quot;</span><span class="p">));</span> <span class="n">printf</span><span class="p">(</span><span class="s">&quot;home: %s &quot;</span><span class="p">,</span> <span class="n">getenv</span><span class="p">(</span><span class="s">&quot;HOME&quot;</span><span class="p">));</span> <span class="n">printf</span><span class="p">(</span><span class="s">&quot;shell: %s &quot;</span><span class="p">,</span> <span class="n">getenv</span><span class="p">(</span><span class="s">&quot;SHELL&quot;</span><span class="p">));</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> </pre></div> <p>Ce qui doit donner quelque chose dans ce gout là :</p> <div class="codehilite"><pre><span class="nv">$ </span>gcc getenv.c <span class="nv">$ </span>./a.out login: phil home: /home/phil shell: /usr/local/bin/zsh </pre></div> <p>Mais le problème c'est que ce qui est mis dans l'environnement par défaut diffère suivant les OS, et même suivant les distributions GNU/Linux ou BSD, ainsi il n'est pas rare de trouver LOGIN à la place de USER et autre subtilités. De plus cette méthode n'est efficace que si l'environnement est bien initialisé lors du lancement du processus. Lancez votre programme avec env -i pour lui envoyer un environnement vide :</p> <div class="codehilite"><pre><span class="nv">$ </span>env -i ./a.out login: <span class="o">(</span>null<span class="o">)</span> home: <span class="o">(</span>null<span class="o">)</span> shell: <span class="o">(</span>null<span class="o">)</span> </pre></div> <p>Pas bon donc. Donc pour faire quelque chose de plus propre il est nécessaire sous UNIX de lire le fichier /etc/passwd, dieu merci les structures et les fonctions qui vont bien sont dans unistd.h et pwd.h</p> <p>Voici la structure passwd qui définie complètement un utilisateur UNIX : man pwd.h</p> <div class="codehilite"><pre><span class="k">struct</span> <span class="n">passwd</span> <span class="p">{</span> <span class="kt">char</span> <span class="o">*</span><span class="n">pw_name</span><span class="p">;</span> <span class="cm">/* user name */</span> <span class="kt">char</span> <span class="o">*</span><span class="n">pw_passwd</span><span class="p">;</span> <span class="cm">/* user password */</span> <span class="n">uid_t</span> <span class="n">pw_uid</span><span class="p">;</span> <span class="cm">/* user ID */</span> <span class="n">gid_t</span> <span class="n">pw_gid</span><span class="p">;</span> <span class="cm">/* group ID */</span> <span class="kt">char</span> <span class="o">*</span><span class="n">pw_gecos</span><span class="p">;</span> <span class="cm">/* real name */</span> <span class="kt">char</span> <span class="o">*</span><span class="n">pw_dir</span><span class="p">;</span> <span class="cm">/* home directory */</span> <span class="kt">char</span> <span class="o">*</span><span class="n">pw_shell</span><span class="p">;</span> <span class="cm">/* shell program */</span> <span class="p">};</span> </pre></div> <p>Donc il nous faut une fonction pour récupérer la structure qui correspond à notre utilisateur. En gros il y a deux fonctions qui font ça (de pwd.h) :</p> <div class="codehilite"><pre><span class="k">struct</span> <span class="n">passwd</span> <span class="o">*</span><span class="n">getpwnam</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">);</span> <span class="k">struct</span> <span class="n">passwd</span> <span class="o">*</span><span class="n">getpwuid</span><span class="p">(</span><span class="n">uid_t</span> <span class="n">uid</span><span class="p">);</span> </pre></div> <p>Donc en gros soit vous connaissez le login de l'utilisateur et vous utilisez la première. Soit vous connaissez l'uid de l'utilisateur et c'est la seconde. Pour avoir l'uid du processus courant il y a uid_t getuid(void); dans unistd.h</p> <p>Donc voici un exemple avec getpwuid qui marchera à tous les coups (sauf en cas de graves problèmes dans le système) :</p> <div class="codehilite"><pre><span class="cp">#include &lt;stdio.h&gt;</span> <span class="cp">#include &lt;sys/types.h&gt;</span> <span class="cp">#include &lt;unistd.h&gt;</span> <span class="cp">#include &lt;pwd.h&gt;</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> <span class="n">uid_t</span> <span class="n">uid</span><span class="p">;</span> <span class="k">struct</span> <span class="n">passwd</span> <span class="o">*</span><span class="n">user</span><span class="p">;</span> <span class="n">uid</span> <span class="o">=</span> <span class="n">getuid</span><span class="p">();</span> <span class="n">user</span> <span class="o">=</span> <span class="n">getpwuid</span><span class="p">(</span><span class="n">uid</span><span class="p">);</span> <span class="n">printf</span><span class="p">(</span><span class="s">&quot;login: %s &quot;</span><span class="p">,</span> <span class="n">user</span><span class="o">-&gt;</span><span class="n">pw_name</span><span class="p">);</span> <span class="n">printf</span><span class="p">(</span><span class="s">&quot;home: %s &quot;</span><span class="p">,</span> <span class="n">user</span><span class="o">-&gt;</span><span class="n">pw_dir</span><span class="p">);</span> <span class="n">printf</span><span class="p">(</span><span class="s">&quot;shell: %s &quot;</span><span class="p">,</span> <span class="n">user</span><span class="o">-&gt;</span><span class="n">pw_shell</span><span class="p">);</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> </pre></div> <p>Compilation et lancement dans un environnement vide :</p> <div class="codehilite"><pre><span class="nv">$ </span>gcc users.c <span class="nv">$ </span>env -i ./a.out login: phil home: /home/phil shell: /usr/local/bin/zsh </pre></div> <p>Et voilà. Attention tout de même, si ce code arrive à se trouver dans une boucle il va lire à chaque fois le fichier /etc/passwd ce qui n'est pas une bonne idée. Donc il peut être utile de stocker ces informations (ces pointeurs) dans une structure globale ou bien de les mettre dans l'environement (putenv, setenv)</p> <p>Enjoy et bon codes.</p> Que font vos processus ? La commande strace http://blog.philpep.org/post/Que-font-vos-processus---La-commande-strace http://blog.philpep.org/post/Que-font-vos-processus---La-commande-strace Sat, 05 Sep 2009 20:27:35 GMT <p>J'ai découvert une commande ulta utile, que ce soit pour la sécurité ou pour débuger : la commande strace.</p> <p>Comme vous le savez, lorsque vous codez en C ou en tout autre langage le code est traduit en langage machine mais il peut aussi faire appel aux fameux "appels systèmes" gérés par l'OS (comme par exemple cloner des processus, accéder à un fichier, afficher quelque chose à l'écran...). Mais comment avoir la trace de toutes ces informations ? La commande strace fait tout cela.</p> <p>Sur *BSD, vous pouvez utiliser de manière analogue <a href="http://www.freebsd.org/cgi/man.cgi?query=truss">truss(1)</a>.</p> <p>Dans la pratique, strace permet de voir ce que fait un processus, on y accède en lui donnant le PID du processus (qu'on peut avoir avec la commande ps). Par exemple si je veux savoir ce que fait mon shell :</p> <div class="codehilite"><pre>% ps 3982 pts/3 00:00:00 zsh <span class="c"># Une fois qu&#39;on a le pid on lance strace (dans un autre shell)</span> % strace -p3982 </pre></div> <p>Regardons un peu ce qu'il se passe pour un simple <em>ls</em> :</p> <div class="codehilite"><pre><span class="c"># Lecture d&#39;un l au clavier</span> <span class="nb">read</span><span class="o">(</span>10, <span class="s2">&quot;l&quot;</span>..., 1<span class="o">)</span> <span class="o">=</span> 1 <span class="c"># écriture du l a l&#39;écran (celui qu&#39;il vient de lire</span> write<span class="o">(</span>10, <span class="s2">&quot;l&quot;</span>..., 1<span class="o">)</span> <span class="o">=</span> 1 <span class="c"># Lecture du s</span> <span class="nb">read</span><span class="o">(</span>10, <span class="s2">&quot;s&quot;</span>..., 1<span class="o">)</span> <span class="o">=</span> 1 <span class="c"># ecriture du s</span> write<span class="o">(</span>10, <span class="s2">&quot;\10ls&quot;</span>..., 3<span class="o">)</span> <span class="o">=</span> 3 <span class="c"># lecture de la touche enter (en C)</span> <span class="nb">read</span><span class="o">(</span>10, <span class="s2">&quot;</span> <span class="s2">&quot;</span>..., 1<span class="o">)</span> <span class="o">=</span> 1 write<span class="o">(</span>10, <span class="s2">&quot;</span> <span class="s2">&quot;</span>..., 2<span class="o">)</span> <span class="o">=</span> 2 alarm<span class="o">(</span>0<span class="o">)</span> <span class="o">=</span> 0 ioctl<span class="o">(</span>10, SNDCTL_TMR_STOP or TCSETSW, <span class="o">{</span>B38400 opost isig icanon <span class="nb">echo</span> ...<span class="o">})</span> <span class="o">=</span> 0 <span class="nb">time</span><span class="o">(</span>NULL<span class="o">)</span> <span class="o">=</span> 1229629587 pipe<span class="o">([</span>3, 4<span class="o">])</span> <span class="o">=</span> 0 gettimeofday<span class="o">({</span>1229629587, 864550<span class="o">}</span>, <span class="o">{</span>0, 0<span class="o">})</span> <span class="o">=</span> 0 <span class="c"># clone ----&gt; un nouveau processus est crée en fait fork() execute l&#39;appel système clone, le nouveau pid est 4024</span> clone<span class="o">(</span><span class="nv">child_stack</span><span class="o">=</span>0, <span class="nv">flags</span><span class="o">=</span>CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, <span class="nv">child_tidptr</span><span class="o">=</span>0xb7ddd998<span class="o">)</span> <span class="o">=</span> 4024 close<span class="o">(</span>4<span class="o">)</span> <span class="o">=</span> 0 <span class="nb">read</span><span class="o">(</span>3, <span class="s2">&quot;&quot;</span>..., 1<span class="o">)</span> <span class="o">=</span> 0 close<span class="o">(</span>3<span class="o">)</span> <span class="o">=</span> 0 rt_sigprocmask<span class="o">(</span>SIG_BLOCK, <span class="o">[</span>CHLD<span class="o">]</span>, <span class="o">[</span>CHLD<span class="o">]</span>, 8<span class="o">)</span> <span class="o">=</span> 0 rt_sigsuspend<span class="o">([])</span> <span class="o">=</span> ? ERESTARTNOHAND <span class="o">(</span>To be restarted<span class="o">)</span> --- SIGCHLD <span class="o">(</span>Child exited<span class="o">)</span> @ 0 <span class="o">(</span>0<span class="o">)</span> --- rt_sigprocmask<span class="o">(</span>SIG_BLOCK, ~<span class="o">[</span>RTMIN RT_1<span class="o">]</span>, <span class="o">[</span>CHLD<span class="o">]</span>, 8<span class="o">)</span> <span class="o">=</span> 0 rt_sigprocmask<span class="o">(</span>SIG_SETMASK, <span class="o">[</span>CHLD<span class="o">]</span>, ~<span class="o">[</span>KILL STOP RTMIN RT_1<span class="o">]</span>, 8<span class="o">)</span> <span class="o">=</span> 0 <span class="c"># La fin du processus fils (4024)</span> wait4<span class="o">(</span>-1, <span class="o">[{</span>WIFEXITED<span class="o">(</span>s<span class="o">)</span> <span class="o">&amp;&amp;</span> WEXITSTATUS<span class="o">(</span>s<span class="o">)</span> <span class="o">==</span> 0<span class="o">}]</span>, WNOHANG|WSTOPPED, <span class="o">{</span><span class="nv">ru_utime</span><span class="o">={</span>0, 0<span class="o">}</span>, <span class="nv">ru_stime</span><span class="o">={</span>0, 0<span class="o">}</span>, ...<span class="o">})</span> <span class="o">=</span> 4024 gettimeofday<span class="o">({</span>1229629587, 867012<span class="o">}</span>, <span class="o">{</span>0, 0<span class="o">})</span> <span class="o">=</span> 0 ioctl<span class="o">(</span>10, SNDCTL_TMR_TIMEBASE or TCGETS, <span class="o">{</span>B38400 opost isig icanon <span class="nb">echo</span> ...<span class="o">})</span> <span class="o">=</span> 0 ioctl<span class="o">(</span>10, TIOCGPGRP, <span class="o">[</span>4024<span class="o">])</span> <span class="o">=</span> 0 ioctl<span class="o">(</span>10, TIOCSPGRP, <span class="o">[</span>3982<span class="o">])</span> <span class="o">=</span> 0 ioctl<span class="o">(</span>10, TIOCGWINSZ, <span class="o">{</span><span class="nv">ws_row</span><span class="o">=</span>38, <span class="nv">ws_col</span><span class="o">=</span>127, <span class="nv">ws_xpixel</span><span class="o">=</span>1270, <span class="nv">ws_ypixel</span><span class="o">=</span>758<span class="o">})</span> <span class="o">=</span> 0 wait4<span class="o">(</span>-1, 0xbfe3f48c, WNOHANG|WSTOPPED, 0xbfe3f434<span class="o">)</span> <span class="o">=</span> -1 ECHILD <span class="o">(</span>No child processes<span class="o">)</span> <span class="c"># Il se demande quelle heure est il :-)</span> <span class="nb">time</span><span class="o">(</span>NULL<span class="o">)</span> <span class="o">=</span> 1229629587 ioctl<span class="o">(</span>10, TIOCSPGRP, <span class="o">[</span>3982<span class="o">])</span> <span class="o">=</span> 0 fstat64<span class="o">(</span>0, <span class="o">{</span><span class="nv">st_mode</span><span class="o">=</span>S_IFCHR|0620, <span class="nv">st_rdev</span><span class="o">=</span>makedev<span class="o">(</span>136, 3<span class="o">)</span>, ...<span class="o">})</span> <span class="o">=</span> 0 fcntl64<span class="o">(</span>0, F_GETFL<span class="o">)</span> <span class="o">=</span> 0x2 <span class="o">(</span>flags O_RDWR<span class="o">)</span> <span class="c"># Il se demande sous quel UID il tourne</span> getuid32<span class="o">()</span> <span class="o">=</span> 1000 <span class="c"># Il reécris le prompt</span> write<span class="o">(</span>1, <span class="s2">&quot;\33]0;phil@philpep.ath.cx ~\7&quot;</span>..., 26<span class="o">)</span> <span class="o">=</span> 26 rt_sigprocmask<span class="o">(</span>SIG_BLOCK, <span class="o">[</span>CHLD<span class="o">]</span>, <span class="o">[</span>CHLD<span class="o">]</span>, 8<span class="o">)</span> <span class="o">=</span> 0 <span class="c"># Il se redemande quelle heure il est</span> <span class="nb">time</span><span class="o">(</span>NULL<span class="o">)</span> <span class="o">=</span> 1229629587 rt_sigaction<span class="o">(</span>SIGINT, <span class="o">{</span>0x80a8fd0, <span class="o">[]</span>, SA_INTERRUPT<span class="o">}</span>, NULL, 8<span class="o">)</span> <span class="o">=</span> 0 write<span class="o">(</span>10, <span class="s2">&quot;\33[1m\33[3m%\33[23m\33[1m\33[0m &quot;</span>..., 149<span class="o">)</span> <span class="o">=</span> 149 <span class="nb">time</span><span class="o">(</span>NULL<span class="o">)</span> <span class="o">=</span> 1229629587 <span class="c"># Il ouvre le fichier /etc/localtime</span> stat64<span class="o">(</span><span class="s2">&quot;/etc/localtime&quot;</span>, <span class="o">{</span><span class="nv">st_mode</span><span class="o">=</span>S_IFREG|0644, <span class="nv">st_size</span><span class="o">=</span>2945, ...<span class="o">})</span> <span class="o">=</span> 0 ioctl<span class="o">(</span>10, FIONREAD, <span class="o">[</span>0<span class="o">])</span> <span class="o">=</span> 0 ioctl<span class="o">(</span>10, TIOCSPGRP, <span class="o">[</span>3982<span class="o">])</span> <span class="o">=</span> 0 ioctl<span class="o">(</span>10, SNDCTL_TMR_STOP or TCSETSW, <span class="o">{</span>B38400 opost isig -icanon -echo ...<span class="o">})</span> <span class="o">=</span> 0 write<span class="o">(</span>10, <span class="s2">&quot;</span> <span class="s2">\33[0m\33[23m\33[24m\33[J\33[01;30m[\33[01;3&quot;</span>..., 105<span class="o">)</span> <span class="o">=</span> 105 write<span class="o">(</span>10, <span class="s2">&quot;\33[K\33[81C \33[01;30m18/12/08 20:46:&quot;</span>..., 46<span class="o">)</span> <span class="o">=</span> 46 <span class="c"># Il attend une nouvelle saisie</span> <span class="nb">read</span><span class="o">(</span>10, Process 3982 detached </pre></div> <p>(Là j'ai viré pas mal de lignes répétitives sur les signaux reçus par le processus)</p> <p>Bon, vous voyez qu'il y a un tas d'appel système, et encore il a fait un fork(), c'est à dire qu'un nouveau processus a été crée... Pour suivre aussi les nouveaux processus il suffit d'un :</p> <div class="codehilite"><pre>% strace -f -pPID </pre></div> <p>Attention, le log peut être très long, donc je vous conseille d'envoyer tout ça dans un fichier, pour donner un ordre d'idée, un simple chargement d'une page dans firefox donne plus de 59000 appels systèmes.</p> <div class="codehilite"><pre>% strace -f -pPID &gt; strace.log 2&gt;&amp;1 </pre></div> <p>Avec ces informations vous pouvez faire du debugage (je me suis rendu compte que mon shell ne fermais pas ses fichiers quand il ne trouvais pas de complétion possible. C'est aussi grâce a ce genres de vérification qu'on s'est rendu compte que le charmant logiciel propriétaire skype ouvrait le fichier bookmaks.html (les favoris de firefox) et les envoyais à la maison mère... C'est une façon rapide de prouver qu'un logiciel propriétaire est malveillant.</p> Une variable qui en contient d'autres en C http://blog.philpep.org/post/Une-variable-qui-en-contient-d-autres-en-C http://blog.philpep.org/post/Une-variable-qui-en-contient-d-autres-en-C Sat, 05 Sep 2009 19:43:52 GMT <p>Vous en avez marre d'avoir des fonctions C avec un maximum de paramètres qui rendent le code illisible. Par exemple :</p> <div class="codehilite"><pre><span class="kt">char</span> <span class="o">*</span><span class="n">prompt</span> <span class="p">(</span><span class="kt">int</span> <span class="n">afficher_nom</span><span class="p">,</span> <span class="kt">int</span> <span class="n">afficher_host</span><span class="p">,</span> <span class="kt">int</span> <span class="n">afficher_pwd</span><span class="p">,</span> <span class="kt">int</span> <span class="n">couleurs</span><span class="p">);</span> <span class="cm">/* ou les afficher_* valent 0 ou 1 suivant si on veut que l&#39;action s&#39;éxecute.) */</span> </pre></div> <p>Clairement en plus d'être imbuvable votre prototype n'est pas bon en terme de mémoire, un int est beaucoup trop grand pour contenir une variable booléenne. Il y a une technique pour n'utiliser qu'une seule variable de type unsigned int qu'on appellera <em>flags</em> (ce nom vous dit peut être déjà quelque chose). On va pouvoir transmettre plusieurs informations dans cette unique variable.</p> <p>NOTA : Je vais juste vous expliquer comment faire dans la pratique, mais je ne vais pas expliquer pourquoi ça marche. La technique s'appuie sur les opérateurs binaires et sur <a href="http://fr.wikipedia.org/wiki/Op%C3%A9rateur_bool%C3%A9en">l'algèbre de Boole</a>.</p> <p>Donc on va mettre ceci dans notre header, le prototype de notre fonction est beaucoup plus lisible :</p> <div class="codehilite"><pre><span class="cp">#ifndef H_HEADER</span> <span class="cp">#define H_HEADER</span> <span class="cp">#define NOM (0)</span> <span class="cp">#define HOST (1)</span> <span class="cp">#define PWD (2)</span> <span class="cp">#define COULEURS (4)</span> <span class="cm">/* Notez que tous les flags doivent être une puissance de 2</span> <span class="cm"> * 0 1 2 4 8 16 32 64 128 512 1024 etc */</span> <span class="cm">/* Prototype de la fonction */</span> <span class="kt">char</span> <span class="o">*</span><span class="n">prompt</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">flags</span><span class="p">);</span> <span class="cp">#endif </span><span class="cm">/* H _HEADER */</span><span class="cp"></span> <span class="cm">/* Pour avoir que flags contienne juste NOM et COULEURS, on fait comme ceci : */</span> <span class="n">flags</span> <span class="o">=</span> <span class="n">NOM</span> <span class="o">|</span> <span class="n">COULEURS</span><span class="p">;</span> <span class="cm">/* Si on veut rajouter un flags (HOST par exemple) : */</span> <span class="n">flags</span> <span class="o">|=</span> <span class="n">HOST</span><span class="p">;</span> <span class="cm">/* Si par la suite on veut supprimer un flags (NOM par exemple) : */</span> <span class="n">flags</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="n">NOM</span><span class="p">;</span> <span class="cm">/* Pour tester si un flags est dans notre variable (COULEURS par exemple) : */</span> <span class="k">if</span> <span class="p">(</span><span class="n">flags</span> <span class="o">&amp;</span> <span class="n">COULEURS</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* Ce test sera vrai seulement si COULEURS est dans flags */</span> <span class="p">}</span> </pre></div> <p>Un dernier petit code pour l'exemple :</p> <div class="codehilite"><pre><span class="cp">#define _GNU_SOURCE</span> <span class="cp">#include &lt;stdio.h&gt;</span> <span class="cp">#include &lt;stdlib.h&gt;</span> <span class="cp">#include &lt;unistd.h&gt;</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">toto</span> <span class="o">=</span> <span class="n">COULEURS</span> <span class="o">|</span> <span class="n">NOM</span> <span class="o">|</span> <span class="n">PWD</span><span class="p">;</span> <span class="kt">char</span> <span class="o">*</span><span class="n">pwd</span> <span class="o">=</span> <span class="n">get_current_dir_name</span><span class="p">();</span> <span class="kt">char</span> <span class="o">*</span><span class="n">prompt</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">pwd</span><span class="p">)</span> <span class="n">free</span><span class="p">(</span><span class="n">pwd</span><span class="p">);</span> <span class="cm">/* On enlève PWD */</span> <span class="k">else</span> <span class="n">toto</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="n">PWD</span><span class="p">;</span> <span class="cm">/* On rajoute HOST */</span> <span class="n">toto</span> <span class="o">|=</span> <span class="n">HOST</span><span class="p">;</span> <span class="cm">/* On appelle la fonction */</span> <span class="n">prompt</span> <span class="o">=</span> <span class="n">prompt</span><span class="p">(</span><span class="n">toto</span><span class="p">);</span> <span class="cm">/* .... */</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> <span class="cm">/* suite du code */</span> </pre></div> <p>Voilà, abusez de cette technique infiniment pratique.</p> <p>Voir à ce sujet <a href="http://www.siteduzero.com/tutoriel-3-32051-les-flags.html">ce tutoriel</a>.</p>