notes·de·pit - Mes doigts dans le clavierParfois j'apprends à pêcher à des gens qui n'aiment pas le poisson2023-06-13T11:41:09+02:00PiTurn:md5:45526db4e4cfb511098640352c276065DotclearVrac #16urn:md5:ac6ab7af0234077bfa302fae1d1573b12023-05-25T10:19:00+02:002023-05-25T11:25:48+02:00PiTMes doigts dans le clavierinutilevrac<p><img src="https://blog.namok.be/public/images/divers/2023/16-michael-jasmund-b3-7ncjxic8-unsplash-1200x.jpg" alt="16-michael-jasmund-b3-7ncjxic8-unsplash-1200x.jpg, mai 2023" style="margin: 0 auto; display: block;" title="une proue de bateau avec 16 écrit dessus" /></p>
<p>Il semble que j'écrive un <em>vrac</em> tous les 4 ans.</p>
<p>Je place des liens dans un fichier et lorsqu'il y en a assez, je publie. Ici, je commence par revérifier si les liens existent encore. <em>Ce blog serait-il mort ?</em></p>
<p><a href="https://pytition.org/"><strong>Pytition</strong></a>, un outil de pétitions libre, écrit en Python à autohéberger. <a href="https://framablog.org/2019/10/25/framapetitions-est-mort-vive-pytition/">Framasoft en parlait «<em>récemment</em> »</a>.</p>
<p><a href="https://joinmobilizon.org/"><strong>Mobilizon</strong></a> est une alternative — à <em>Eventbrite</em> et consorts pour ne pas le citer — pour la publication d'évènements et la gestion des inscriptions. L'outil a bien évolué ces dernières années, il est <em>acentralisé</em> et plusieurs instances existent — recensées sur <a href="https://mobilizon.rog">mobilizon.org</a>. On y trouve par exemple <a href="fttps://mobilizon.fr">mobilizon.fr</a>…</p>
<p>Si vous avez besoin d'un correcteur d'orthographe (<em>spoiler alert</em> on en a tous besoin), <a href="https://languagetool.org/fr">Languagetool</a> vérifie l'orthographe et la grammaire. Il propose aussi un <em>plugin</em> Firefox. Plus d'excuse.</p>
<p>À la <a href="https://jamstack.org/generators/">longue liste de <strong>générateurs de sites</strong></a> s'ajoutent</p>
<ul>
<li><p><a href="https://www.getlektor.com/"><strong>Lektor</strong></a> découvert dans LinuxPratique 101 et dont la particularité est d'avoir une interface web pour rédiger. C'est du Python et voici le <a href="https://github.com/lektor/lektor">dépôt Github</a></p></li>
<li><p><a href="https://honkit.netlify.app/"><strong>Honkit</strong></a> est plutôt dédié à la génération de documentation (c'est plus basique. Moins beau). C'est du nodejs, voici le <a href="https://github.com/honkit/honkit">dépôt Github</a> et un <a href="https://tutorial.djangogirls.org/fr/">exemple chez Django Girls</a></p></li>
</ul>
<p>Tu te demandes si tu as une bonne <strong>vitesse de frappe</strong> au clavier ? Tu voudrais te comparer à tes camarades ? José, un <em>student</em> a développé <a href="https://dactylo.bruxelles.dev">dactylo.bruxelles.dev</a>.</p>
<p>Un nième outil de tableau blanc ? <a href="https://browserboard.com"><strong>Browserboard</strong></a>.</p>
<p>Un <strong>terminal</strong>, si tu n'en n'as pas un <em>bon</em>. <a href="https://tabby.sh/"><strong>Tabby</strong></a>. C'est peut-être lourd, il faut tester. Voici le <a href="https://github.com/Eugeny/tabby">dépôt Github</a>.</p>
<p>Un dernier pour jouer est un site créant <strong>différentes sortes de cadenas</strong> permettant d'ouvrir un texte, une image, un son, une vidéo ou un lien. <a href="https://lockee.fr/"><strong>Lockee</strong></a>. Un <a href="https://lockee.fr/o/i38depnX">essai</a> ?</p>
<p><br/><br/></p>
<p><em>Crédit photo chez par <a href="https://unsplash.com/fr/@jasmund">Michael Jasmund</a></em></p>
Les gars et les garcesurn:md5:93f14d42d4adacfc46d7b43c4f4d02712021-11-24T19:09:00+01:002021-11-25T13:32:04+01:00PiTMes doigts dans le clavierecriture-inclusiveinternetinutile<p><img src="https://blog.namok.be/public/images/divers/2021/criatura__by_cristina_otero_d3imedp_1200.jpg" alt="criatura__by_cristina_otero_d3imedp_1200.jpg, nov. 2021" style="margin: 0 auto; display: block;" /></p>
<p>Dans un soucis d'inclusion j'écris souvent et je dis « Salut à tous et à toutes ».</p>
<p>L'écriture inclusive m'intéresse beaucoup parce que c'est à la mode sans doute. Parce que ça m'amuse de jouer avec la langue surtout. Pas vraiment parce qu'elle permet d'inclure car je ne suis pas (encore) convaincu que c'est l'écriture inclusive qui inclut. Je crois qu'il est bon d'être attentif à toutes nos différences et essayer d'accueillir l'autre avec les siennes, avec les miennes. Essayer d'être accueillant, ce n'est pas tenter, à priori, de n'oublier personne. De les citer toutes. Mais aujourd'hui, ça parle principalement d'une seule différence. On parlera de masculin / féminin (ou féminin / masculin si l'on préfère) dans l'écriture et le langage.</p>
<p>Rappel : l'écriture inclusive, ce n'est pas <em>que</em> le point médian '<code>·</code>'. Le point médian est le dernier recours lorsque l'on écrit. Je préfère donc d'abord utiliser « tous et toutes » (la double flexion) et parfois, s'il le faut, si c'est obligé, s'il n'y a rien d'épicène, écrire alors « toustes » ou « tous·tes » (jamais « tou·te·s » car <em>trop de points médians tuent le point médian</em>). Pour en savoir plus, un article auquel j'agrée en tous points.</p>
<p>Écriture inclusive : faisons le point autour de la cheminée<br />
<a href="https://www.24joursdeweb.fr/2017/ecriture-inclusive-faisons-le-point-autour-de-la-cheminee/">https://www.24joursdeweb.fr/2017/ecriture-inclusive-faisons-le-point-autour-de-la-cheminee/</a></p>
<p>Bref, si je disais « salut à tous » — ou « salut <em>tertous</em> » dans le patois local — que j'ai remplacé par « salut à tous et à toutes », j'aime bien changer parfois et dire « salut les gars ». Ça fait <em>coooool</em>, moins sérieux, un peu plus familier… Dans un soucis d'inclusion <em>bla bla</em>, et en étant (un peu) moins sérieux, j'ose donc :</p>
<p>« <strong>Salut les gars et les garces</strong> ».</p>
<p>Les réactions sont diverses :</p>
<ul>
<li>un sourire quand on accepte cet humour ; </li>
<li>un sourire nerveux que l'on force parce que c'est sans doute mieux de <em>faire bien</em> par rapport à l'injonction machiste et que l'on est prise de court ; </li>
<li>un froncement de sourcil qui dit : « heu, c'est pas drôle » ;</li>
<li>un haussement de sourcil qui dit : « <em>seriously</em> » ;</li>
<li>aucune, j'ai le nez dans mon smartphone.</li>
</ul>
<p>Finalement est-ce de l'humour — que l'on peut trouver déplacé ou pas — ou être en avance (ou en retard) sur son temps ? (Comme beaucoup je me plais à croire être un précurseur, un lanceur… d'idioties…). Hier, je regardais <a href="https://orthogrenoble.net/mots-nouveaux-dictionnaires/">les nouveaux mots qui arrivent dans le dictionnaire</a> et je vois qu'il s'y trouve des mots qui existent déjà mais dont le sens s'est étoffé. Par exemple, <a href="https://www.projet-voltaire.fr/divers/nouveaux-mots-du-petit-larousse-et-du-petit-robert-2022-que-disent-ils-de-nous/">cluster, écouvillon ou confinement</a>.</p>
<p>Je regarde aussi la définition de garce en cherchant les plus gentilles,</p>
<ol>
<li><p>dans <a href="https://dictionnaire.lerobert.com/definition/garce">Le Robert</a>, le sens 1 dit</p>
<blockquote>
<p><em>Vieux</em> Femme, fille</p>
</blockquote></li>
<li><p>dans le <a href="https://www.larousse.fr/dictionnaires/francais/garce/36082">Larousse</a>, le sens 3 dit</p>
<blockquote>
<p> Jeune fille ou femme en général, souvent avec une nuance admirative pour son aspect physique : Une belle garce.</p>
</blockquote></li>
<li><p>dans le centre national de ressources textuelles et lexicales (<a href="https://www.cnrtl.fr/definition/garce">cnrtl</a>) le sens A1 dit </p>
<blockquote>
<p>I. − Subst. fém.
A. − [Les subst. masc. corresp. sont garçon et gars] Synon. (jeune) fille. Vx. Adolescente.<br />
Elle (...) retrouva des jambes de jeune garce, s'occupa des papiers de sa nièce (Zola, Terre,1887, p. 384).<br />
De son temps encore, les petits gars et les petites garces du village venaient faire, en manière de jeu, le diable et la belle Orberose (A. France, Île ping.,1908, p. 178).</p>
</blockquote></li>
</ol>
<p>Bon, c'est pas gagné !</p>
<p>Je décide de me baser sur… les définitions qui m'arrangent, celle du <a href="https://dictionnaire.lerobert.com/definition/garce">Robert</a> et celle du <em><a href="https://www.cnrtl.fr/definition/garce">cnrtl</a></em>. Je citerai même Anatole France s'il le faut pour essayer que l'an prochain, les dictionnaires étoffent le mot <em>garce</em> d'une nouvelle définition basée sur la <a href="https://www.cnrtl.fr/definition/gars">définition de <em>gars</em></a>.</p>
<blockquote>
<ol>
<li>GARCE <em>[ɡaʁs]</em> n.f. (Nom masculin correspondant, <em>gars</em>) Fille, jeune femme. Fille qui inspire la sympathie, qui ne fait pas d'histoires. <em>Une bonne, une brave garce; une jeune garce</em>. Familier : <em>Salut les garces !</em></li>
</ol>
</blockquote>
<p>À la lumière de cette nouvelle définition, que personne ne soit surpris<sup id="fnref:f1"><a href="https://blog.namok.be/?post/2021/11/24/les-gars-et-les-garces#fn:f1" rel="footnote">1</a></sup> si je lance un tonitruant « Salut les gars et les garces » c'est que les personnes concernées m'inspirent la sympathie…</p>
<p><br/></p>
<p><em>Crédit photo par <a href="https://www.deviantart.com/cristina-otero/art/criatura-212676685">Christina</a>. À défaut d'un haussement de sourcil, un œil réprobateur.</em></p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:f1">
<p>Je remplace « ne soyez pas surpris » que j'aurais du <em>double-flexer</em> ou affubler d'un point médian par « Que personne ne soit surpris ». Pour moi, c'est ça une écriture inclusive. <a href="https://blog.namok.be/?post/2021/11/24/les-gars-et-les-garces#fnref:f1" rev="footnote">↩</a></p>
</li>
</ol>
</div>
fzf, fuzzy finder ou comment faire des recherches plus efficaces dans son terminalurn:md5:c0e3e9320d984859824ab4d57524fb6c2021-06-08T17:04:00+02:002021-06-08T16:19:35+02:00PiTMes doigts dans le clavieralternativesdebianlinuxpratique<p><img src="https://blog.namok.be/public/images/divers/2021/98__magnifying_glass____vii_by_salihagir_d2nyby1-fullview.jpg" alt="98__magnifying_glass____vii_by_salihagir_d2nyby1-fullview.jpg, juin 2021" style="margin: 0 auto; display: block;" /></p>
<p><code>fzf</code> pour <em>command line fuzzy finder</em>… qui ne veut rien dire pour moi<sup id="fnref:f1"><a href="https://blog.namok.be/?post/2021/06/08/fzf%2Cfuzzy-finder-ou-comment-faire-des-recherches-plus-efficaces-dans-son-terminal#fn:f1" rel="footnote">1</a></sup> mais
continuez de lire, c'est pratique.</p>
<p><em>First at all</em>, ça s'installe d'un simple coup de <code>apt install fzf</code>.</p>
<p><a href="https://github.com/junegunn/fzf">Sur la page du projet</a>, on peut voir que c'est un <strong>filtre unix</strong> qui
peut utiliser toutes sortes de listes. Pas uniquement une liste de fichiers,
mais aussi l'historique, une liste de processus, de noms de domaines…</p>
<p><code>fzf</code> lit la liste à partir de l'entrée standard <code>STDIN</code> et affiche l'élément sélectionné (ou les éléments sélectionnés) sur la sortie standard (<code>STDOUT</code>).</p>
<pre><code class="language-bash">
find * -type f | fzf [-m]
fzf
</code></pre>
<p>cet exemple envoie la liste de tous les fichiers du répertoire courant (sans les
fichiers cachés) à la commande fzf et la recherche peut commencer. Sans
paramètre la commande <code>fzf</code> a ce comportement par défaut.</p>
<p>Par défaut le recherche est étendue (<em>extended search mode</em>) et se fait en
entrant des mots séparés par des espaces. Certains caractères sont spéciaux et
signifient : <code>^</code> au début de la chaine, <code>$</code> à la fin, <code>!</code> inverse et <code>'</code> pour
<em>exact match</em>.</p>
<p>Par exemple : <code>^music mp3$ 'exact oui !non</code> retourne les éléments
qui commence par « music », se termine par « mp3 », contiennent la chaine
« exact », les lettres de « oui » et pas celles de « non ».</p>
<p><img src="https://blog.namok.be/public/images/divers/2021/fzf/fzf-1.gif" alt="fzf-1.gif, juin 2021" style="margin: 0 auto; display: block;" /></p>
<ul>
<li><code>[Up]</code> et <code>[Down]</code> pour voyager dans la liste ; </li>
<li><code>[Enter]</code> pour sélectionner l'élément ;</li>
<li>l'option <code>-m</code> permet de faire une recherche multiple. La sélection se fait avec la touche <code>[Tab]</code> et la validation avec <code>[Enter]</code> ;</li>
<li>l'option <code>-e</code> (<em>exact match</em>) désactive la recherche étendue.</li>
</ul>
<p>Il est possible de faire une recherche dans l'historique tout aussi facilement</p>
<pre><code class="language-bash">
history | fsf +s --tac
</code></pre>
<ul>
<li><code>+s</code> ne trie pas la liste ;</li>
<li><code>--tac</code> la montre dans l'ordre inverse.</li>
</ul>
<p>Plutôt que d'envoyer sur <em>STDOUT</em> (ce qui n'est pas pratique), il est possible
de rediriger la commande avec un bon vieil <code>xargs</code> :</p>
<p><img src="https://blog.namok.be/public/images/divers/2021/fzf/fzf-2.gif" alt="fzf-2.gif, juin 2021" style="margin: 0 auto; display: block;" /></p>
<p>… ou d'utiliser un sous-shell <code>vim -o $(fzf)</code>.</p>
<p>Plutôt que d'utiliser un sous-shell, il est possible d'utiliser <strong><code>**[Tab]</code></strong> pour lancer <code>fzf</code> à partir d'un répertoire.</p>
<pre><code class="language-bash">
vim /elsewhere/**[Tab]
cd /**[Tab]
</code></pre>
<p><img src="https://blog.namok.be/public/images/divers/2021/fzf/fzf-4.gif" alt="fzf-4.gif, juin 2021" style="margin: 0 auto; display: block;" /></p>
<p>Pour que le lancement de <code>fzf</code> se fasse avec <code>**[Tab]</code>, il faut éduquer bash en ajoutant ceci à son <code>.bashrc</code>.</p>
<pre><code class="language-bash">
source /usr/share/bash-completion/completions/fzf
</code></pre>
<p>La <em>magie des deux étoiles</em> fonctionne également pour une liste d'hôtes accédés en <code>ssh</code> ou pour des identifiants de processus (<em>pid</em>). Essayez:</p>
<pre><code class="language-bash">
ssh [Tab]
ssh **[Tab]
kill -9 [Tab]
</code></pre>
<p>Pour les personnes qui le préfèrent, il existe des touches de raccourcis (<em>key
binding</em>) que l'on peut définir en incluant le fichier d'exemple (ou en écrivant
le sien).</p>
<pre><code class="language-bash">
source /usr/share/doc/fzf/examples/key-bindings.bash
</code></pre>
<p>… par défaut <code>[Ctrl-T]</code> lancera <code>fzf</code>.</p>
<p><code>[Ctrl-r]</code> remplacera la recherche dans l'historique par une recherche dans
l'historique avec <code>fzf</code>. Beaucoup plus facile.</p>
<p><code>[Alt-c]</code> permettra de faire un changement de répertoire (<code>cd</code>) vers le répertoire sélectionné par <code>fzf</code>.</p>
<p>Il est maintenant temps de s'occuper des paramètres que l'on peut passer à la
commande. En voici quelques uns issus de la présentation officielle que je
trouve pratiques.</p>
<ul>
<li><code>--height 50%</code> pour limiter la hauteur de fzf à 50% du terminal ; </li>
<li><code>--multi</code> (<code>-m</code>) en mode multi-sélection ;</li>
<li><code>--inline-info</code> pour gagner une ligne, la recherche et le nombre de fichiers se trouvent sur la même ligne ; </li>
<li><code>--preview cat {}</code> pour afficher une fenêtre de prévisualisation du fichier. La documentation propose un peu mieux avec: </li>
</ul>
<pre><code class="language-bash">
--preview '[[ $(file --mime {}) =~ binary ]] && echo {} is a binary file \
|| cat {} 2>/dev/null | head -300`
</code></pre>
<ul>
<li><code>--preview-window='hidden'</code> qui n'affiche pas la fenêtre de prévisualisation… <em>heu !</em></li>
<li>… <code>--bind='f2:toggle-preview'</code> qui associe à la touche <code>[F2]</code> l'affichage, ou pas, de la prévisualisation ;</li>
</ul>
<p><img src="https://blog.namok.be/public/images/divers/2021/fzf/fzf-5.gif" alt="fzf-5.gif, juin 2021" style="margin: 0 auto; display: block;" /></p>
<ul>
<li><code>--bind='ctrl-a:select-all+accept</code> en combinaison avec <code>--multi</code> bien sûr ;</li>
</ul>
<p>…ce qui peut s'ajouter dans son fichier de configuration <em>bash</em> (ou autre <em>shell</em>):</p>
<pre><code class="language-bash">
export FZF_DEFAULT_OPTS="--height 50% \
--multi \
--inline-info \
--preview '[[ \$(file --mime {}) =~ binary ]] \
&& echo {} is a binary file \
|| bat --color=always --style=plain,changes {} \
2>/dev/null | head -300 ' \
--preview-window='hidden' \
--bind='f2:toggle-preview,ctrl-a:select-all+accept'"
</code></pre>
<p>Si l'on veut une prévisualisation d'un répertoire, cela peut se faire <em>via</em> : </p>
<pre><code class="language-bash">
export FZF_ALT_C_OPTS="--preview 'tree -C {} | head -100'"
</code></pre>
<p>Si vous avez des <em>tips and tricks</em>, n'hésitez pas à les partager…</p>
<p><br/></p>
<p><em>Sources, la <a href="https://github.com/junegunn/fzf">page officielle du projet</a> et la <a href="https://www.youtube.com/watch?v=qgG5Jhi_Els">vidéo associée</a> ainsi que <a href="https://sim590.github.io/fr/outils/fzf/">cet article</a>.</em><br />
<em>Crédit photo <a href="https://www.deviantart.com/salihagir/art/98-Magnifying-Glass-VII-161165305">salihagir</a>.</em></p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:f1">
<p><a href="https://blog.namok.be/?post/2021/06/08/deepl.com">deepl</a> traduit <em>fuzzy finder</em> par « détecteur de fausses pistes » ou encore « chercheur de flou ». <a href="https://blog.namok.be/?post/2021/06/08/fzf%2Cfuzzy-finder-ou-comment-faire-des-recherches-plus-efficaces-dans-son-terminal#fnref:f1" rev="footnote">↩</a></p>
</li>
</ol>
</div>
Donne-moi ton adresse echaturn:md5:b522470fe5bc291783eda4acbe823b7a2021-04-12T08:39:00+02:002021-04-12T08:39:00+02:00PiTMes doigts dans le clavieralternativesdiversgafamgeekinternetlogiciellibre<p><img src="https://blog.namok.be/public/images/divers/2021/mike-meyers--haAxbjiHds-unsplash.jpg" alt="mike-meyers--haAxbjiHds-unsplash.jpg, avr. 2021" style="margin: 0 auto; display: block;" title="un téléphone (vieux modèle) jaune sur fond jaune" /></p>
<p>— Donne moi ton adresse echat, s'il-te-plait.<br />
— Voici : pit@example.chat</p>
<p>Ce devrait être aussi simple de donner son adresse <em>echat</em> (pour <em>eletronic chat</em>) que l'adresse d'un site web à consulter ou que son adresse <em>email</em> (<em>electronic mail</em>) voire que son numéro de téléphone.</p>
<p>L'email fonctionne très bien depuis plus de 20 ans. Tout le monde y est — y était — habitué. Il suffit de choisir deux choses: premièrement, une adresse email et donc un fournisseur et deuxièmement un programme pour lire et écrire ses mails.</p>
<p>Il existe pléthore de fournisseurs de mails (les <em>gafam</em> et d'autres), et quelques programmes pour les lire (les <em>gafam</em> et les autres). Aujourd'hui, malheureusement, beaucoup de personnes utilisent une adresse <em>gmail</em> et le programme <em>gmail</em>. Oui, ça porte le même nom… pour apporter de la confusion. <a href="https://fr.wikipedia.org/wiki/Don%27t_be_evil"><em>Don't be evil</em> qu'ils disaient !</a> Si le mail n'était pas si résistant, Google l'aurait déjà mangé.</p>
<p>Pour le <em>chat</em>, ce qui était avant la communication instantanée et qui est maintenant une « communication instantanée qui peut être asynchrone et plus du tout instantanée », c'est un peu plus compliqué même s'il s'agit toujours d'une question de <strong>choix</strong>. Il faut choisir trois choses : en plus de l'adresse (et donc d'un fournisseur) et du programme, il faudra choisir le protocole. Là où pour le mail, le protocole est unique, pour le <em>chat</em>, il y en a plusieurs et c'est là que le bât blesse. C'est parce qu'il y a plusieurs protocoles que je ne peux pas à partir de <em>Whatsapp</em> t'envoyer un message sur <em>Messenger</em>.</p>
<p>— De toute façon, on m'a dit d'utiliser <em>Signal</em> !<br />
— Oui et on en revient à cette question de <strong>choix</strong>.</p>
<p>L'être humain est tel que s'il ne peut pas choisir, il râle… et quand il doit choisir, il ne sait pas choisir ! La personne humaine est compliquée. J'en suis une.</p>
<p>L'inconvénient de cette histoire de protocole, c'est que tu ne peux pas choisir un programme pour tes messages quels que soient les choix de tes amis et amies. <em>Amis politiques, il faudrait forcer les gros (<em>aka</em> les GAFAM) a utiliser un protocole commun (<em>XMPP</em> ou <em>Matrix</em> ou…)</em>. Certains de tes amis et amies te forcent à utiliser un programme particulier. « Libre » à toi de l'accepter ou pas. De les convaincre ou pas et l'on sait très bien que c'est difficile.</p>
<p>Revenons a cette histoire de <strong>choix</strong>.</p>
<p>Si le choix se porte sur un « ± gros centralisé », il ne faudra faire qu'un seul choix et les autres seront alors imposés. Une fois l'application (le programme) choisie (<em>Whatsapp</em>, <em>Facebook</em>, <em>Messenger</em>, <em>Instagram</em>… <em>Signal</em>), le choix du protocole et du fournisseur suivent.</p>
<p>Pour un choix acentralisé, pour ne pas choisir un gros (<em>aka</em> GAFAM), il faudra porter plusieurs choix… et ça c'est difficile !</p>
<ul>
<li>choisir un protocole: XMMP ou Matrix ou… </li>
<li>choisir le fournisseur et son adresse. Où ? Comment trouver ?</li>
<li>choisir une application. Laquelle est « la mieux » ? </li>
</ul>
<p>… bref… à l'heure où j'écris ces lignes, je conseille <a href="https://signal.org/fr/"><em>Signal</em></a>. Un seul choix. Le moins pire.</p>
<p><br/></p>
<p><i>Photo by <a href="https://unsplash.com/@mike_meyers?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Mike Meyers</a> on <a href="https://unsplash.com/s/photos/communication?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></i></p>
tmux à l'usage d'un profurn:md5:f155fb22d2c87d3ed06de5e04a0a573a2020-12-03T14:14:00+01:002020-12-03T20:19:43+01:00PiTMes doigts dans le clavieresiinutilelinuxlogiciellibre<p><img src="https://blog.namok.be/public/images/divers/2020/cadres_by_thoum_d2458vb.jpg" alt="cadres_by_thoum_d2458vb.jpg, déc. 2020" style="margin: 0 auto; display: block;" /></p>
<p><a href="https://github.com/tmux/tmux/wiki"><code>tmux</code></a> est un multiplexer de terminal — oui, ce billet est un tout petit
peu technique — permettant d'ouvrir une session sur une machine et de s'en
détacher en laissant la session ouverte (un peu comme <code>screen</code>).</p>
<p>Dans l'usage quotidien<sup id="fnref:f1"><a href="https://blog.namok.be/?post/2020/12/03/tmux-a-l-usage-d-un-prof#fn:f1" rel="footnote">1</a></sup> de ma machine, j'utilise régulièrement un terminal
et j'ai un répertoire par « activité » : un répertoire pour tel cours, un
répertoire pour telle organisation… Un peu comme tout le monde j'imagine excepté
les personnes qui <em>mettent tout sur le bureau</em> !</p>
<p>Lorsque je me consacre à un cours par exemple, je peux être dans le répertoire
du cours pour trouver un énoncé, dans un autre terminal, être dans le répertoire
pour les corrections… j'utilise donc plusieurs terminaux pour un même cours.
Comme je travaille tantôt sur un cours, tantôt sur un autre cours et que j'ai
plusieurs bureaux virtuels, ça devient vite désorganisé.</p>
<p>Pendant tout un temps, je retenais que le premier bureau, c'est pour tel cours,
le second pour un autre, le cinquième pour les navigateurs internet, le sixième
pour la communication instantanée et mail, etc. Depuis quelques temps,
j'utilise <code>tmux</code> pour les terminaux et je conserve cette habitude de laisser
dans mon bureau virtuel 6 le mail et irc et dans le 5, les navigateurs internet.</p>
<p><code>tmux</code> me permet d'ouvrir une session par cours. Dans cette session, je peux
ouvrir plusieurs terminaux. Les intérêts de <code>tmux</code> sont pour moi :</p>
<ul>
<li>la possibilité de pouvoir me détacher et attacher une session. Ce qui me
permet de ne plus être lié à un bureau virtuel pour une activité ;</li>
<li>la possibilité de <em>découper</em> ma fenêtre en plusieurs ;</li>
<li>…</li>
</ul>
<p>Bref, voici mon aide-mémoire de commandes.</p>
<p><code>tmux</code> lance une session (numérotée) mais c'est mieux de directement la nommer
avec l'option <code>-t</code> :</p>
<pre><code class="language-">
tmux new -t <name>
</code></pre>
<p>Pour lister les sessions existantes :</p>
<pre><code class="language-">
tmux ls
</code></pre>
<p>Pour rejoindre — s'attacher à — une session existante :</p>
<pre><code class="language-bash">
tmux a [-t <name>]
tmux a [-t <number>]
</code></pre>
<p>Pour tuer toutes les sessions <code>tmux</code>, il suffit de tuer le serveur. Il est aussi
possible de n'en tuer qu'une par son nom ou son numéro :</p>
<pre><code class="language-">
tmux kill-server
tmux kill-session -t <name>
tmux kill-session -t <number>
</code></pre>
<p>Dès lors que l'on se trouve dans une session <code>tmux</code>, un préfixe défini permet de
lancer une commande. <code>Ctrl-b</code> est le préfixe par défaut. Dans la suite, <em>prefix</em>
signifie chez moi <code>Ctrl-b</code>. Vous pourrez le redéfinir mais pour moi — en bépo —
c'est accessible.</p>
<ul>
<li><em>prefix</em> <code>d</code> se détache de la session courante ;</li>
<li><em>prefix</em> <code>:</code> permet d'écrire une commande <em>tmux</em> ;</li>
<li><em>prefix</em> <code>$</code> (re)nomme la session courante ;</li>
<li><em>prefix</em> <code>)</code> passe une autre session (la suivante « à droite ») ;</li>
<li><em>prefix</em> <code>(</code> passe une autre session (la suivante « à gauche ») ;</li>
<li><em>prefix</em> <code>s</code> liste les sessions (comme <code>tmux ls</code> mais en étant attaché) et
permet de passer d'une session à l'autre ;</li>
</ul>
<h3>Fenêtre</h3>
<p>Une session <code>tmux</code> se compose d'une ou plusieurs fenêtres. Une fenêtre occupe
tout l'écran du terminal.</p>
<ul>
<li><em>prefix</em> <code>,</code> renommer la fenêtre ;</li>
<li><em>prefix</em> <code>c</code> créer une nouvelle fenêtre ;</li>
<li><em>prefix</em> <code>n</code> aller à la fenêtre suivante ;</li>
<li><em>prefix</em> <em>X</em> aller à la fenêtre <em>X</em> ;</li>
<li><em>prefix</em> <code>w</code> liste les fenêtres et permet de les parcourir et d'en choisir
une ;</li>
<li><em>prefix</em> <code>esperluette</code> supprime la fenêtre courante (et tous ses panels) ;</li>
</ul>
<h3>Panels</h3>
<p>Une fenêtre peut être découpée en <em>morceaux</em> / carrés / <em>panels</em>.</p>
<ul>
<li><em>prefix</em> <code>%</code> scinde le panel en deux verticalement ;</li>
<li><em>prefix</em> <code>=</code> scinde le panel en deux horizontalement (par défaut c'est <code>"</code>
mais j'ai changé) ;</li>
<li><em>prefix</em> <em>flèche</em> passe d'un panel à l'autre dans la direction de la flèche ;</li>
<li><em>prefix</em> <code>Ctrl-</code><em>flèche</em> redimensionne le panel courant dans le sens de la
flèche ;</li>
<li><em>prefix</em> <code>o</code> échange les panels ;</li>
<li><em>prefix</em> <code>;</code> retourne au dernier panel ;</li>
<li><em>prefix</em> <code>x</code> supprime le panel courant</li>
</ul>
<p>Il est possible d'exécuter les mêmes actions dans tous les panels en même temps.
Pour ce faire, entrer la commande <code>:setw synchronize-panes on</code> (et <code>off</code> quand
c'est terminé).</p>
<p>Ensuite, on s'amuse à écrire un fichier de configuration en fonction des
habitudes que l'on a. Le <a href="https://github.com/Pinkilla/dotfiles">mien se trouve là</a>.</p>
<ul>
<li>quand je ferme un <em>pane</em> ou une fenêtre, je n'ai pas besoin de confirmer ;</li>
</ul>
<pre><code class="language-">
# kill pane without confirm
bind-key x kill-pane
# new window in some directory
bind-key c new-window -c "#{pane_current_path}"
</code></pre>
<ul>
<li>je redéfinis le <em>split</em> horizontal en demandant d'ouvrir le nouveau panel dans
le répertoire courant ;</li>
</ul>
<pre><code class="language-">
# split window verticaly and horizontaly
bind-key = split-window -v -c "#{pane_current_path}"
bind-key % split-window -h -c "#{pane_current_path}"
</code></pre>
<ul>
<li>j'augmente la taille du buffer pour l'historique ;</li>
</ul>
<pre><code class="language-">
# big history (default is 2000)
set -g history-limit 5000
</code></pre>
<p>Voilà, pour le reste, nous verrons à l'usage…</p>
<p><br/></p>
<p><em>Crédit photo chez DeviantArt par <a href="https://www.deviantart.com/thoum/art/Cadres-127895591">Thoum</a></em></p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:f1">
<p>Je sais que le mot est un peu tombé en désuétude au profit de
<em>journalier</em>… que je trouve beaucoup moins joli. Oui les mots peuvent être
beaux. <a href="https://blog.namok.be/?post/2020/12/03/tmux-a-l-usage-d-un-prof#fnref:f1" rev="footnote">↩</a></p>
</li>
</ol>
</div>
Commento pour gérer les commentairesurn:md5:3c06dcb80dd3bafeb728c2ea81b089592020-06-08T22:09:00+02:002020-06-08T21:26:57+02:00PiTMes doigts dans le clavieralternativesblogdebiandisqusinutilelogiciellibre<p><img src="https://blog.namok.be/public/images/divers/2020/grue-arcenciel-20200605-cut.jpg" alt="grue-arcenciel-20200605-cut.jpg, juin 2020" style="margin: 0 auto; display: block;" /></p>
<p>Une fois tous les 10 ans, je repasse sur le système de commentaires disponible
sur mon blog (non, un blog, ce n'est pas <em>hasbeen</em>).</p>
<p>Au début, j'utilisais le système de commentaires de <a href="//dotclear.org">Dotclear</a> que j'ai
abandonné pour mauvaise gestion des spams et pour suivre la mode de <a href="//disqus.com">Disqus</a>.
Depuis, <a href="https://open-time.net/tag/antispam">Frank a fait pas mal de boulot pour la gestion des
spams</a> mais le mal était fait. <a href="//example.org/blog/?post/2013/01/02/disqus-pour-les-commentaires">J'ai
installé Disqus</a> qui a deux défauts assez importants : c'est
propriétaire et centralisé. Disqus est même doublement centralisé : il n'y a
« qu'un seul » serveur et tous les commentaires que fait un utilisateur ou une
utilisatrice se retrouvent au même endroit. Il est donc assez facile de savoir
que j'ai commenté <em>example.com</em>, le site du boulot et <em>example.org</em>, le site de
pétanque du village. C'est (encore) plus facile pour profiler<sup id="fnref:f1"><a href="https://blog.namok.be/?post/2020/06/08/commento#fn:f1" rel="footnote">1</a></sup>.</p>
<p>Ensuite, comme quasi personnes ne commentent sur mon blog, j'ai désactivé les
commentaires et personne ne s'est plaint. C'est dire ;-)</p>
<p><em>Il pleut.</em></p>
<p>Je décide donc d'installer <a href="//commento.io"><strong>Commento</strong></a>. C'est la version <em>logiciel
libre</em> de Disqus. Il est possible de souscrire un hébergement chez eux ou de
l'installer. Je l'installe. Vous me direz ce que vous en pensez.</p>
<p>Je suis <em>grosso-merdo</em> la <a href="//docs.commento.io/installation/self-hosting/on-your-server/release-binaries.html">documentation
officielle</a>
qui m'a posé quelques soucis. Voici la procédure .</p>
<p>Le site a besoin de <strong>PostgreSQL</strong>.</p>
<pre><code class="sh">
# apt install postgresql
</code></pre>
<p>C'est une bonne pratique de créer un utilisateur spécifique pour l'application,
je crée donc un utilisateur <code>commento</code> qui aura les droits qui vont bien — et
pas plus — sur la base de données (BD) :</p>
<pre><code class="sh">
# sudo -u postgres psql
postgres=# create database commento;
CREATE DATABASE
postgres=# create role commento;
CREATE ROLE
postgres=# alter role commento with login;
ALTER ROLE
postgres=# \password commento
</code></pre>
<p><a href="https://docs.commento.io/getting-started/self-hosting/releases.html">Je télécharge
Commento</a>
et commence l'installation.</p>
<ol>
<li><p>désarchiver dans <code>/opt/commento</code><sup id="fnref:f2"><a href="https://blog.namok.be/?post/2020/06/08/commento#fn:f2" rel="footnote">2</a></sup></p></li>
<li><p>l'idée du logiciel est d'utiliser les variables d'environnement pour le
configurer, je décide d'écrire un script qui positionnera les variables
d'environnement et qui lancera le service.</p>
<p>C'est un service qui tourne sur un certain port. Il faudra donc un serveur
web et un <em>virtual host</em>.</p>
<pre><code class="sh">
root@momos:~# cat /opt/commento.sh
#!/bin/bash
export COMMENTO_ORIGIN=https://commento.example.org
export COMMENTO_PORT=8081
export COMMENTO_POSTGRES=postgres://commento:secret@localhost:5432
/commento?sslmode=disable
export COMMENTO_CDN_PREFIX=$COMMENTO_ORIGIN
export COMMENTO_SMTP_HOST=relay.example.org
export COMMENTO_SMTP_PORT=25
export COMMENTO_SMTP_FROM_ADDRESS=commento@example.org
export COMMENTO_FORBID_NEW_OWNERS=true
/opt/commento/commento &
</code></pre>
<p><br/></p></li>
<li><p>mise en place d'un virtual host pour que le service réponde à l'adresse
<code>commento.example.org</code>. (J'utilise <code>dehydrated</code> pour la gestion du certificat
SSL avec <em>LetsEncrypt</em>.)</p>
<pre><code class="apache">
<VirtualHost *:80\>
ServerName http://commento.example.org
ServerAdmin webmaster@example.org
ProxyRequests Off
ProxyPass / http://127.0.0.1:8081/
ProxyPassReverse / http://commento.example.org:8081/
# Dehydrated
Alias /.well-known/acme-challenge
/var/lib/dehydrated/acme-challenges
<Directory /var/lib/dehydrated/acme-challenges>
Options None
AllowOverride None
<IfModule mod_authz_core.c>
Require all granted
</IfModule>
</Directory>
ErrorLog ${APACHE_LOG_DIR}/
commento.example.org-error.log
CustomLog ${APACHE_LOG_DIR}/
commento.example.org-access.log combined
</VirtualHost>
<VirtualHost *:443>
ServerName https://commento.example.org
ServerAdmin webmaster@example.org
ProxyRequests Off
ProxyPass / http://127.0.0.1:8081/
ProxyPassReverse / https://commento.example.org:8081/
SSLEngine on
SSLCertificateFile /var/lib/dehydrated/certs/
commento.example.org/fullchain.pem
SSLCertificateKeyFile /var/lib/dehydrated/certs/
commento.example.org/privkey.pem
# Dehydrated
Alias /.well-known/acme-challenge
/var/lib/dehydrated/acme-challenges
<Directory /var/lib/dehydrated/acme-challenges>
Options None
AllowOverride None
<IfModule mod_authz_core.c>
Require all granted
</IfModule>
</Directory>
LogLevel warn
ErrorLog ${APACHE_LOG_DIR}/
commento.example.org-error.log
CustomLog ${APACHE_LOG_DIR}/
commento.example.org-access.log combined
</VirtualHost>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
</code></pre>
<p><br/></p></li>
<li><p>pour terminer, ce serait bien que <code>systemd</code> se charge du service.</p>
<pre><code class="sh">
# cat /lib/systemd/system/commento.service
[Unit]
Description=Commento daemon service
After=multi-user.target
[Service]
User=commento
Type=forking
WorkingDirectory=/tmp
ExecStart=/opt/commento.sh
[Install]
WantedBy=multi-user.target
</code></pre></li>
</ol>
<p>Et tout roule. Vous pouvez laisser un commentaire. Normalement ;-)</p>
<p><br/></p>
<p><em>Crédit photo perso. Hier une grue et un arc-en-ciel devant la maison.</em></p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:f1">
<p>Choisis un <a href="https://disqus.com/by/meenuspeenus/comments/">un profil au
hasard</a> et observe. On peut voir
les sites sur lesquels le profil commente régulièrement et ce qu'il y dit. <a href="https://blog.namok.be/?post/2020/06/08/commento#fnref:f1" rev="footnote">↩</a></p>
</li>
<li id="fn:f2">
<p>En fait, je désarchive dans <code>/opt/commento-1.8.0</code> et je fais un lien soft
<code>commento</code>. <a href="https://blog.namok.be/?post/2020/06/08/commento#fnref:f2" rev="footnote">↩</a></p>
</li>
</ol>
</div>
Monitoring Moodle : script pour telegraf et dashboard pour Grafanaurn:md5:72e385aed047331a76549a8b1f70fb682020-06-07T16:21:00+02:002020-10-07T07:50:08+02:00PiTMes doigts dans le clavierdebianenseignementesigeeklogiciellibremoodle<p><img src="https://blog.namok.be/public/images/divers/2020/xiaolong-wong-9gpERFdyDW4-unsplash-1200x.jpg" alt="xiaolong-wong-9gpERFdyDW4-unsplash-1200x.jpg, juin 2020" style="margin: 0 auto; display: block;" /></p>
<p>Pour monitorer la machine et le service de <em>elearning</em> <strong>Moodle</strong>, il existe
moult manières de faire. L'une d'entre elles est d'utiliser la pile
<strong>telegraf-InfluxDB-Grafana</strong> :</p>
<ul>
<li><em>telegraf</em> pour la récolte d'informations sur la machine faisant tourner
<em>moodle</em> ;</li>
<li><em>InfluxDB</em> pour stocker ses informations et ;</li>
<li><em>Grafana</em> pour l'aspect visuel et les beaux graphiques. </li>
</ul>
<p><a href="https://blog.namok.be/public/images/divers/2020/Firefox_Screenshot_2020-06-07T08-56-27.765Z.png" title="Firefox_Screenshot_2020-06-07T08-56-27.765Z.png, juin 2020"><img src="https://blog.namok.be/public/images/divers/2020/.Firefox_Screenshot_2020-06-07T08-56-27.765Z_m.png" alt="Firefox_Screenshot_2020-06-07T08-56-27.765Z.png, juin 2020" style="margin: 0 auto; display: block;" /></a></p>
<p><a href="https://blog.namok.be/public/images/divers/2020/Firefox_Screenshot_2020-06-07T09-01-08.267Z.png" title="Firefox_Screenshot_2020-06-07T09-01-08.267Z.png, juin 2020"><img src="https://blog.namok.be/public/images/divers/2020/.Firefox_Screenshot_2020-06-07T09-01-08.267Z_m.png" alt="Firefox_Screenshot_2020-06-07T09-01-08.267Z.png, juin 2020" style="margin: 0 auto; display: block;" /></a></p>
<p>L'objet de cet article est <em>l'affichage du nombre d'utilisateurs et
d'utilisatrices connectées</em> à la plateforme et, en cadeau, le <em>dashboard</em> que
j'utilise pour <em>Moodle / PostgreSQL</em>.</p>
<h3>Aller chercher le nombre d'utilisateurs et utilisatrices connectées et remplir la BD</h3>
<p>À ma connaissance, il n'y a pas de moyen d'obtenir le nombre de personnes
connectées au temps <em>t</em> mais bien entre le temps <em>t1</em> et <em>t2</em>. Comme je n'ai pas
trouvé évident d'utiliser l'API pour ce faire, suivant <a href="https://moodle.org/mod/forum/discuss.php?d=404772">un conseil sur le forum
Moodle</a>, j'ai choisi d'aller chercher l'info dans la base de données
(BD).</p>
<p>C'est dans la table <code>mdl_user</code> que se trouvent les informations de connections.
Il suffit de compter les personnes qui se sont connectées « avant maintenant »
et « après maintenant moins un peu de temps ».</p>
<p>Un script <code>bash</code> pourrait ressembler à :</p>
<pre><code class="sh">
$ cat moodleusers.sh
#!/bin/bash
timestamp_now=$(date --utc +"%s")
timestamp_start=$(($timestamp_now - 60))
QUERY="SELECT COUNT(*) FROM mdl_user
WHERE deleted=0 AND lastaccess > "${timestamp_start}"
AND lastaccess < "${timestamp_now}" ;"
RESULT=$(psql -d 'moodle' -c "${QUERY}" 2>/dev/null)
RESULT_CUT=$(echo $RESULT | cut -d ' ' -f 3)
echo "nUsersMoodle nUsersMoodle=${RESULT_CUT}"
</code></pre>
<ul>
<li>les <em>timestamps</em> sont donnés en GMT ;</li>
<li>le script affiche le résultat qui sera compréhensible par <em>InfluxDB</em></li>
<li>je ne suis pas parvenu à obtenir le résultat dans une seule variable (je ne
suis pas assez bon avec <em>bash</em>). j'ai donc fait <em>RESULT</em> puis <em>RESULT_CUT</em>.</li>
</ul>
<p>C'est <em>telegraf</em> qui va lancer le script. C'est donc l'utilisateur <code>telegraf</code>
qui devra avoir les droits de lecture dans la BD pour la table <code>mdl_user</code>. Ce
qui peut se faire comme suit :</p>
<pre><code class="sh">
# sudo -u postgres psql -d moodle
moodle =# create role telegraf
moodle =# alter role telegraf with login
moodle =# grant telegraf to moodle
moodle =# grant select on mdl_user to telegraf
</code></pre>
<p>Après vérification que le script est bien fonctionnel et que l'utilisateur
<code>telegraf</code> peut le lancer, il reste à l'ajouter à <code>telegraf</code> comme suit :</p>
<pre><code class="sh">
# vim /etc/telegraf/telegraf.conf
[[inputs.exec]]
# ## Commands array
commands = [
"/elsewhere/moodleusers.sh"
]
# ## Timeout for each command to complete.
timeout = "5s"
data_format = "influx"
interval = "60s"
</code></pre>
<ul>
<li>demander à <em>telegraf</em> de recharger son fichier de conf ;</li>
<li>vérifier que tout fonctionne bien ;</li>
<li>attendre un peu que la BD InfluxDB se remplisse. </li>
</ul>
<h3>Faire de beaux graphes</h3>
<p>Il reste à récupérer cette information via <em>Grafana</em> et l'inclure dans son
<em>dashboard</em>. J'en ai profité pour proposer <a href="https://grafana.com/grafana/dashboards/12418">un dashboard Moodle with
postgres</a>.</p>
<p>Nous verrons à l'usage, s'il est fonctionnel.</p>
<p>Je suis preneur de tous vos retours et corrections.
<em>Enjoy !</em></p>
<p><br/></p>
<p><em>Crédit photo par <a href="https://unsplash.com/photos/9gpERFdyDW4">Xiaolong Wong</a> chez Unsplash. Une orange bien découpée
fait un beau camembert.</em></p>
Vrac #15 - Bonne année 2019urn:md5:4f0712d1c7f9d70b64b450340c10cd5e2019-03-07T18:49:00+01:002019-09-15T23:02:31+02:00PiTMes doigts dans le clavierinutilevrac<p><img src="https://blog.namok.be/public/images/divers/2019/nr-15.jpg" alt="nr-15.jpg" style="margin: 0 auto; display: block;" title="nr-15.jpg, mar. 2019" /></p>
<p>Ça fait bien longtemps que je n'ai plus écrit un <em>vrac</em> — 4 ans quand même — et
ce <em>vrac</em> servira de vœux de nouvelle année.</p>
<p>Lorsque je décidais d'écrire un <em>vrac</em>, c'était pour partager des liens pour
lesquels je n'écrirais pas d'article mais dont je voulais me souvenir et que je
voulais partager ici. Dans mon <em><a href="http://namok.be/blog/?post/2014/04/01/vrac-14">vrac
14</a></em>, je parlais de <strong>pandoc</strong>…
et bien, depuis 4 ans, je l'utilise quasi tous les jours.</p>
<p>Qu'ai-je découvert de révolutionnaire depuis ?<br />
Rien.</p>
<p>Depuis mon dernier <em>vrac</em>, ce qui a le plus évolué sur internet, c'est
l'attitude de chacun et chacune sur les réseaux sociaux. Nous avons développé
une capacité à nous émouvoir très fort. Pendant peu de temps. Et — quasi — sans
action derrière. Certaines personnes utilisent de plus en plus les réseaux
sociaux tandis que d'autres parlent de <strong>déconnexion</strong>. Dans ce domaine, 2017,
2018 voit émerger <em>Activity pub</em> et des réseaux comme Mastodon. Décentralisés.
Rien de neuf par contre en terme d'engouement du public. <em>Les gens restent sur
les plateformes centralisées. GAFAM and co.</em></p>
<p>Personnellement, j'utilise de moins en moins. Je ne me connecte sur FB qu'une
fois par mois… voire tous les deux mois. Je l'utilise uniquement (quasi) pour
les pages que je gère… et je le fais avec <a href="http://hootsuite">hootsuite</a> que je
vous conseille. Je reste un peu dépendant de Twitter et Mastodon mais je me
soigne.</p>
<p>Et quelques liens quand même pour cloturer ce court billet :</p>
<ul>
<li><p>Apprentissage de la programmation (en français)
<a href="http://www.france-ioi.org/">france-ioi.org/</a>;</p></li>
<li><p>France IOI est un équivalent de <a href="http://code.org">code.org</a>;</p></li>
<li><p>Un site qui permet de visualiser les allocations / désallocations de mémoire
dans quelques langages et pas uniquement python comme son nom l'indique ;
<a href="http://pythontutor.com">pythontutor.com</a>. De plus, il est possible de
demander de l'aide à quelqu'un en ligne. Pratique.</p></li>
<li><p><a href="http://www.epoptes.org">epoptes</a> et <a href="https://veyon.io">veyon</a> pour déporter les bureaux dans une salle de
classe . C'est dans le <em>pipe</em> depuis 3 ans.</p></li>
</ul>
<p><br/><br/></p>
<p><em>Crédit photo chez <a href="http://gratisography.com">Gratisography</a></em></p>
Debian GNU/Linux sur le Microsoft Storeurn:md5:655df9ced7fe2e2ba04bb8748c1992852018-10-05T08:56:00+02:002018-10-05T07:58:14+02:00PiTMes doigts dans le clavierdebianesigeekjavapratique<p><img src="https://blog.namok.be/public/images/divers/2018/debian-app-4.jpg" alt="debian-app-4.jpg" style="margin: 0 auto; display: block;" title="debian-app-4.jpg, oct. 2018" /></p>
<p><strong>Debian GNU/Linux est disponible sur le Microsoft Store.</strong></p>
<p>Pour apprendre les bases linux sans effort d'installation — fini le <em>dual boot</em>
et les machines virtuelles — il est possible d'utiliser l'application Debian
Linux — ou une autre distribution — qui se trouve sur le Microsoft Store !</p>
<p><img src="https://blog.namok.be/public/images/divers/2018/.debian-app_m.jpg" alt="debian-app.jpg" style="margin: 0 auto; display: block;" title="debian-app.jpg, oct. 2018" /></p>
<p>J'ai eu l'occasion de mettre les mains sur une machine Microsoft Windows 10 et
j'ai donc pu constater l'existence de <em>Microsoft Store</em>. C'est une bonne chose.
J'ai ensuite installé « l'application Debian GNU/Linux ». Notez la présence de
« GNU » qui fera plaisir à <em>rms</em>.</p>
<p>Une fois installée, Microsoft signale qu'il faut activer le <strong>WSL</strong> <em>Windows
Subsystem for Linux</em> ou encore <em>Sous-système Windows pour Linux</em>. Ça se fait en
deux clics et un reboot… parce que bon, un <em>reboot</em> sous Windows, c'est toujours
bien.</p>
<p><img src="https://blog.namok.be/public/images/divers/2018/debian-app-23.jpg" alt="debian-app-23.jpg" style="margin: 0 auto; display: block;" title="debian-app-23.jpg, oct. 2018" /></p>
<p>Un fois le reboot fait, on peut profiter d'une console Debian sous Windows. Si
vous cherchez un peu, vous verrez que sans installation et avec simplement <em>WSL</em>
activé, vous avez accès à <em>bash</em>. Vous verrez aussi qu'il existe d'autres
distribution proposées par Microsoft.</p>
<p><a href="https://blog.namok.be/public/images/divers/2018/debian-app-4.jpg" title="debian-app-4.jpg"><img src="https://blog.namok.be/public/images/divers/2018/.debian-app-4_m.jpg" alt="debian-app-4.jpg" style="margin: 0 auto; display: block;" title="debian-app-4.jpg, oct. 2018" /></a></p>
<p>Windows propose une <em>console linux</em> et pas une distribution complète. Pas
d'environnement de bureau par exemple. L'usage sera donc <em>textuel</em><sup id="fnref:f1"><a href="https://blog.namok.be/?post/2018/10/05/debian-microsoft#fn:f1" rel="footnote">1</a></sup>.</p>
<p>Je salue l'initiative de Microsoft tout en me disant que cette console Linux
dans l'environnement Windows, bien que très pratique, ne va pas faciliter la
compréhension des concepts « systèmes » dans les têtes des débutants et
débutantes. Nous avons donc bien affaire à la possibilité d'inclure un sous
système Linux dans un système Microsoft Windows. Même s'il ne s'agit pas d'une
machine virtuelle bien emballée, ce n'est pas un système Linux natif non plus.</p>
<p>— Qu'est-ce que WSL ?</p>
<p>WSL, <em>Windows Subsystem Linux</em> est un ensemble de modules permettant d'exécuter
des applications Linux <em>elf64</em> sous Microsoft Windows. <a href="https://blogs.msdn.microsoft.com/wsl/2016/04/22/windows-subsystem-for-linux-overview/">Voir la présentation
générale (en) sur le blog msdn</a>.</p>
<p>La notion de sous-système n'est pas neuve puisque Win32, par exemple, est un
sous-système de Microsoft Windows NT (NT dans la suite) qui présente l'interface
de programmation aux applications. Ce sous-système exécute les appels systèmes
NT appropriés en fonction de l'API qu'il présente.</p>
<p>Les binaires Linux sont lancés dans des processus particuliers — les <em>processus
pico</em> — au lieu des <em>processus NT</em>. Au contraire des processus NT qui embarquent
le sous-système Win32, les processus pico embarquent les pilotes <em>lxss.sys</em>
et <em>lxcore.sys</em> qui se chargent de la traduction des appels systèmes Linux en
appels systèmes NT et de l'émulation du noyau Linux. <a href="https://blogs.msdn.microsoft.com/wsl/2016/05/23/pico-process-overview/">Plus d'info sur les
processus pico (en) sur le blog de msdn</a> et <a href="https://blogs.msdn.microsoft.com/wsl/2016/06/08/wsl-system-calls/">sur l'émulation du noyau
Linux (en) sur le même blog</a>.</p>
<p>Chaque processus lancé à partir de WSL — via bash.exe ou l'application Debian
— est un processus apparaissant dans la liste des processus Windows. C'est un
<em>processus pico</em> et non un <em>processus NT</em>.</p>
<p><a href="https://docs.microsoft.com/en-us/windows/wsl/faq">Pour les questions, la FAQ Microsoft est dispo</a>.</p>
<p><em>Crédit photo faite maison.</em></p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:f1">
<p>Il est toujours possible d'installer un serveur X sur Windows et
d'exporter son <em>display</em> « à l'ancienne » mais ce n'est pas offciellement
supporté. <a href="https://blog.namok.be/?post/2018/10/05/debian-microsoft#fnref:f1" rev="footnote">↩</a></p>
</li>
</ol>
</div>
Colorer son lessurn:md5:778e3f0618701d914e31b7a609cd31f92018-09-21T08:12:00+02:002019-04-01T19:59:00+02:00PiTMes doigts dans le clavierdebianlinux<p><img src="https://blog.namok.be/public/images/divers/2018/less.png" alt="less.png" style="margin: 0 auto; display: block;" title="less.png, sept. 2018" /></p>
<p>Ce matin, je faisais une recherche dans une page de <code>man</code> et je ne voyais pas le
mot cherché. Ma page de manuel était blanche sur noir. Triste. Ajoutons des
couleurs.</p>
<p>Pour info, lorsque l'on consulte une page de manuel, linux lance un <em>pager</em> par
défaut. Ce programme permet de visualiser un fichier <em>page par page</em>
(<em>page par page</em>, <em>pager</em>, tu l'as ?) et y faire des recherches. Les principaux
sont: <em>less</em>, <em>more</em> et <em>most</em>. Par défaut, c'est souvent <em>less</em> qui est
installé.</p>
<p>La première manière de colorer ses pages de manuel est d'utiliser un pager qui
gère les couleurs. Lapalissade. <em>most</em> est coloré par défaut, il s'active en
ajoutant dans son <code>.bashrc</code>.</p>
<pre><code class="language-bash">
export PAGER=most
</code></pre>
<p>Si, par contre, vous avez l'habitude avec <em>less</em> et que vous ne voulez pas
changer, vous pouvez lui apprendre les couleurs et ajouter dans votre <code>.bashrc</code>
(la première ligne le configure un peu différemment. <code>man less</code> pour la
signification des options):</p>
<pre><code class="language-bash">
export LESS='-F -X -i -J -m -R -W -n -x4 -z-6'
export LESS_TERMCAP_mb=$'\E[1;31m' # begin bold
export LESS_TERMCAP_md=$'\E[1;36m' # begin blink
export LESS_TERMCAP_me=$'\E[0m' # reset bold/blink
export LESS_TERMCAP_so=$'\E[1;47m' # begin reverse video
export LESS_TERMCAP_se=$'\E[0m' # reset reverse video
export LESS_TERMCAP_us=$'\E[1;32m' # begin underline
export LESS_TERMCAP_ue=$'\E[0m' # reset underline
export PAGER=less
</code></pre>
<p>Cette configuration se trouve facilement sur le net, j'ai par contre choisis du
<em>blanc gras</em> sur <em>gris</em> pour le <em>reverse</em> afin de bien voir les mots cherchés.</p>
<p>Enjoy</p>
Hier je me suis demandé si Chatroulette existait encore ou caméra virtuelleurn:md5:e59c822f1b46b5eec0eddb5c22b548422018-05-15T18:55:00+02:002018-05-15T18:03:25+02:00PiTMes doigts dans le clavierdiversgeekinutile<p><img src="https://blog.namok.be/public/images/divers/2018/Fotolia_84018856_S.jpg" alt="girl with laptop computer " style="margin: 0 auto; display: block;" title="girl with laptop computer , mai 2018" /></p>
<p>Si vous avez une caméra sur votre <em>laptop</em>, vous l'avez probablement recouverte
d'un papier… soit parce que vous êtes parano soit parce que vous vivez nu·e soit
parce que vous ne l'utilisez en fait jamais.</p>
<p>Le petit papier sur la caméra c'est bien. C'est facile. On ne voit pas ce qu'il
se passe chez toi (éventuellement à ton insu). Par contre — petite digression
— si tu es à peine parano, il faudrait surtout être sûr que ton micro ne
fonctionne pas… mais c'est une autre paire de manches et un autre débat.</p>
<p>La caméra donc.</p>
<p>Pour une raison qui m'échappe je me suis rappelé l'existence de Chatroulette.
<a href="http://namok.be/blog/?post/2010/02/15/%C3%80-la-recherche-du-chat-sur-la-roulette">Pour rappel, (re)lisez « À la recherche du chat sur la roulette »</a>.
Ça date de 2010 — huit ans déjà — et, je voulais savoir si le service était
toujours là. En huit ans, on aurait pu le perdre et cela aurait été dommage.
Vraiment. <em>#sarcasm</em>. C'était novateur à l'époque. Conceptuellement et
techniquement. Pouvoir discuter par caméra interposée directement dans son
navigateur était assez neuf. Et si l'interlocuteur est choisi aléatoirement, ça
peut faire de belles rencontres.</p>
<p>Que nenni !</p>
<p>L'idée de parler et de voir une personne à l'autre bout du monde ou de son
quartier pour discuter a immédiatement glissé sur « je montre ma bite ». Quasi
tous les articles que vous lirez sur Chatroulette vous le confirmeront, dès que
vous passez plus de dix minutes à appuyer sur <em>next</em> — bim — une bite.</p>
<p>Est-ce encore vrai aujourd'hui ? Question importante s'il en est.</p>
<p>Je me rends donc à l'adresse <a href="https://blog.namok.be/?post/2018/05/15/chatroulette.com">chatroulette.com</a> pour voir et
je vois qu'il faut d'abord sourire à la caméra pour se connecter. Plus question
donc de laisser l'autocollant sur la caméra et, si vous êtes de ceux ou celles
qui veulent montrer une autre partie de leur corps ensuite, vous n'êtes pas
sûr·es que le serveur n'enregistre pas tout le flux et pourra conserver votre
tête et… le reste. Comment faire pour ne pas montrer sa tête ? Mettre une photo
devant la cam pourrait sans doute faire l'affaire mais il faut peut-être que
« ça bouge ». Bref.</p>
<p>Ça c'est l'introduction, je glisse maintenant sur la partie <em>geek</em> plus
intéressante.</p>
<p><strong>Comment faire pour envoyer autre chose que le flux capté par ma caméra ?</strong>.</p>
<p>Quelques recherches plut tard avec des mots clés comme "caméra virtuelle" ou
"split caméra" ou autres, je trouve évidemment que la solution existe et est
très simple à mettre en œuvre (voir <a href="https://askubuntu.com/questions/881305/is-there-any-way-ffmpeg-send-video-to-dev-video0-on-ubuntu">AskUbuntu</a> ou <a href="https://video.stackexchange.com/questions/12905/repeat-loop-input-video-with-ffmpeg/16933#16933">Stackexchange</a>) ce
qui est moins évident. Il existe un module pour le noyau linux qui va émuler une
caméra.</p>
<ul>
<li>Installer</li>
</ul>
<pre><code class="language-bash">
# apt install v4l2loopback-utils
# modprobe v4l2loopback
</code></pre>
<ul>
<li>Lister les périphériques</li>
</ul>
<pre><code class="language-bash">
# v4l2-ctl --list-devices
Dummy video device (0x0000) (platform:v4l2loopback-000):
/dev/video1
Integrated Camera (usb-0000:00:18.0-2):
/dev/video0
</code></pre>
<p>Et je vois apparaitre un <em>device</em> (périphérique) supplémentaire « Dummy
video device » qui et une caméra virtuelle. Lorsque je me connecte à un site
qui me demande accès à ma caméra, il me suffit de choisir celle-la.</p>
<ul>
<li>Envoyer un flux vidéo à cette caméra virtuelle. </li>
</ul>
<p>Si votre but est d'envoyer une vidéo, vous pouvez utiliser la commande
suivante:</p>
<pre><code class="language-bash">
ffmpeg -re -i /elsewhere/beautifulvideo.mp4 -f v4l2 /dev/video1
</code></pre>
<p>Si votre but est de travailler avec quelqu'un et de lui montrer votre
bureau, vous pouvez utiliser la commande suivante:</p>
<pre><code class="language-bash">
ffmpeg -f x11grab -r 15 -s 1280x720 -i :0.0+0,0
-vcodec rawvideo -pix_fmt yuv420p -threads 0
-f v4l2 /dev/video1
</code></pre>
<p>Pour reprendre l'exemple de <em>Chatroulette</em> et si ça vous amuse, vous pouvez
maintenant faire plaisir aux utilisateurs qui ont leur membre en main en leur
proposant un film qui va bien… pour peu que ça vous intéresse de voir leur
réaction… moi non.</p>
<p>Ce billet me sert d'aide-mémoire.</p>
<p><br/></p>
<p><em>Crédit photo chez <a href="http://deviantart.com">Fotolia</a>. J'avais acheté quelques crédits qui
arrivent maintenant à échéance. Qu'est-ce donc qui étonne cette dame ?</em></p>
Peertubeurn:md5:e296b7fa230542570dc7e5908cd2fe2e2018-03-23T11:08:00+01:002018-03-23T11:41:21+01:00PiTMes doigts dans le clavieralternativesmastodonpeertube<p><img src="https://blog.namok.be/public/images/divers/2018/pears-pexels-photo-175767-1200.jpg" alt="pears-pexels-photo-175767-1200.jpg" style="margin: 0 auto; display: block;" title="pears-pexels-photo-175767-1200.jpg, mar. 2018" /></p>
<p>Ce 21 mars 2018, avec le printemps, apparait la première version de
<strong>peertube</strong>. C'est une bonne nouvelle !</p>
<p>Peertube surfe sur la vague de la décentralisation et plus particulièrement de
l'<em>acentralisation</em>. Peertube est une alternative libre aux plateformes
centralisées et propriétaires de diffusion de vidéos (Youtube surement,
Dailymotion sans doute et Viméo peut-être).</p>
<p>— Donc, on oublie Youtube et on utilise Peertube ?</p>
<p>Oui, c'est l'idée mais pas tout a fait. L'inconvénient de peertube — on verra
que ce n'en n'est pas un, juste une petite barrière à l'entrée — pour
l'utilisateur c'est qu'il va devoir choisir une <em>instance</em>. Il va devoir
choisir un site web sur lequel il ira poster ses vidéos. Il n'existe pas
<a href="https://www.qwant.com/?q=peertube%20instance&t=all"><code>peertube.com</code></a> mais plein d'autres. Ben oui, c'est acentralisé.</p>
<p><strong>Avant la génération Youtube</strong>, les gens hébergeaient leurs vidéos sur leur site
web / blog et la partageaient sur leur site web / blog. Il n'y avait pas de
réseaux sociaux d'ailleurs. Je sais, c'est vieux.</p>
<p><strong>Pendant les dix dernières années</strong>, ça se passait un peu comme ça.</p>
<p>— T'as vu la vidéo de <em>un truc super</em> ?<br />
— Où ?<br />
— Ben, sur Youtube bien sûr.</p>
<p><strong>Maintenant</strong> ou peut-être demain.</p>
<p>— T'as vu la vidéo de <em>un truc super</em> ?<br />
— Où ? Sur <code>peertube.moe</code> ?<br />
— Non, elle est chez <code>framatube.org</code></p>
<p>… ce qui ne change pas grand chose si l'on partage un lien.</p>
<p>Quelques avantages de Peertube:</p>
<ul>
<li><p>c'est un logiciel libre qui apporte ses 4 libertés fondamentales (utilisation,
étude, modification et redistribution);</p></li>
<li><p>je peux partager la vidéo que je veux tant que c'est légal;</p></li>
<li><p>je peux choisir de déposer ma vidéo sur une instance existante ou, si je
préfère, je peux installer ma propre instance;</p></li>
<li><p>les instances — les différents sites web
<a href="https://peertube.moe"><code>peertube.moe</code></a>, <a href="https://framatube.org"><code>framatube.org</code></a>,
<a href="https://video.lqdn.fr"><code>video.lqdn.fr</code></a>,
<a href="https://video.colibris-outilslibres.org"><code>video.colibris-outilslibres.org</code></a>… — peuvent communiquer entre elles;</p>
<p>Ça c'est génial. Si Alice dépose une vidéo sur le site <code>peertube.xyz</code> où elle
s'est créé un compte, Bob peut la trouver sur le site <code>share.tube</code> où il
n'est même pas obligé d'avoir un compte. Et la vidéo est toujours sur le
site d'origine, elle est simplement référençée sur tous les autres sites.</p></li>
<li><p>chaque instance peut choisir ses propres règles;</p></li>
<li><p>un peu technique, le protocole utilisé (transfert pair-à-pair) supporte la
charge sans besoin de gros moyens;</p>
<p>Si ta vidéo devient virale, tout ceux qui la regarde la partage aussi un peu
et tu n'as pas besoin d'un gros serveur.</p></li>
<li><p>une intégration avec Mastodon (<a href="https://framatube.org/videos/watch/da2b08d4-a242-4170-b32a-4ec8cbdca701">voir la vidéo</a>);</p></li>
</ul>
<p><em>Peertube</em> c'est bon, mangez en, informez vous et posez vos questions.
Si vous êtes une association, une école, un société posez vous la question de
<em>monter votre propre instance</em>.
Pour aller plus loin parce que j'ai été très bref, il suffit de lire un peu le
<em>framablog</em>:</p>
<ul>
<li><a href="https://framablog.org/2018/03/21/peertube-beta-une-graine-dalternative-a-youtube-vient-declore/">« Peertube beta: une graine d'alternative à Youtube vient d'éclore » sur le blog de Framasoft</a></li>
<li><a href="https://framablog.org/2018/03/22/peertube-ya-deja-plein-de-videos/">« Premières instance et liens utiles » sur le blog de Framasoft</a></li>
<li><a href="https://joinpeertube.org/fr/">Join peertube</a> (pour démarrer, un lien à partager)</li>
<li><a href="https://instances.peertu.be/instances">Liste des intances Peertube</a></li>
<li><a href="https://github.com/Chocobozzz/PeerTube">Le dépôt github de Peertube par Chocobozzz</a></li>
</ul>
<p></br></p>
<p><em>Crédit photo chez <a href="https://www.pexels.com/photo/3-pear-fruits-175767/">Pexels par Clem</a>. Quelques poires, différentes,
imparfaites mais c'est bon.</em></p>
JDK9, les nouveautésurn:md5:1c05ae39e1f7e69af1622d5ad0b5664f2018-03-22T17:02:00+01:002018-05-07T15:28:57+02:00PiTMes doigts dans le clavierenseignementesigeekjava<p><img src="https://blog.namok.be/public/images/divers/2018/unzipped_egg_by_pixelduster_cut.jpg" alt="unzipped_egg_by_pixelduster_cut.jpg" style="margin: 0 auto; display: block;" title="unzipped_egg_by_pixelduster_cut.jpg, mar. 2018" /></p>
<p><a href="https://blog.namok.be/?post/2018/03/22/JDK9#installation">Préalables pratiques</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#of">List, Set, Map of</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#modules">Les modules, <em>Java Platform Module System (JPMS)</em> ou encore <em>Jigsaw</em></a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#processus">Gestion des processus systèmes</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#jshell">Jshell</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#stream">Nouvelles méthodes pour la classe Stream</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#javadoc">Javadoc</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#underscore">_ underscore est un mot clé</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#optional">La classe <em>Optional</em> se dote de nouvelles méthodes</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#version">Numérotation des versions</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#sha">Algorithmes de hashages supplémentaires</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#string">Réduction de l'espace de stockage des chaines</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#unicode">Support d'unicode 8.0</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#http2">http2</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#inputstream">La classe <em>InputStream</em> se dote également de nouvelles méthodes</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#flow">La classe <code>Flow</code>, le producteur et le consommateur</a><br />
<a href="https://blog.namok.be/?post/2018/03/22/JDK9#more">Quelques points non détaillés</a></p>
<hr />
<p>Alors que <em>JDK 10</em> est sorti le 20 mars, je vous parle de <em>JDK 9</em>.
Chaque chose en son temps…</p>
<h2 id="installation">Préalables pratiques</h2>
<p>Pour tester JDK9, j'utilise <strong>Netbeans</strong>. Netbeans 8.1 et Netbeans 8.2 ne
permettent pas d'utiliser JDK9. Il est nécessaire d'installer une version de
développement <a href="http://wiki.netbeans.org/JDK9Support">comme indiqué dans le wiki</a> et <a href="https://stackoverflow.com/questions/42734944/netbeans-8-2-with-jdk9-build160">sur Stackoverflow</a>
pour celles et ceux qui préfèrent.</p>
<p>Il est nécessaire d'installer un JDK9. Sans blague. J'installe <em>jdk-9.0.4</em>.</p>
<p>Dans la suite, je supposerai que mon installation se trouve dans
<strong><code>/usr/lib/jvm/jdk9</code></strong>. Je regarde l'arborescence — via la commande <code>tree</code>
— qui a l'allure suivante :</p>
<p><a href="https://blog.namok.be/public/images/divers/2018/jdk9/jdk9-1.png" title="jdk9-1.png"><img src="https://blog.namok.be/public/images/divers/2018/jdk9/.jdk9-1_m.png" alt="jdk9-1.png" style="margin: 0 auto; display: block;" title="jdk9-1.png, mar. 2018" /></a></p>
<p>On constate d'emblée que l'organisation des fichiers a changé… ce qui est le
corollaire immédiat de la notion de modules — voir ci-dessous — introduite dans
le JDK.</p>
<p>Je repère aussi le fichier <code>src.zip</code> que je décompresse aussitôt et que je
conserve au chaud pour plus tard. Son organisation est semblable
à l'organisation en module du répertoire <em>jmods</em>.</p>
<p>J'essaierai de citer les JEP, <em>JDK Enhancement Proposals</em> en rapport avec les
différents points.</p>
<p>Dans certains exemples, je suppose l'existence d'une classe <code>Video</code> représentant
une vidéo. Une vidéo a un auteur, un titre, un nombre de <em>likes</em> et un état
publiée ou pas. Je suppose également, l'existence d'une <em>factory</em> associées:
<code>Videos</code>.</p>
<h2 id="of">List, Set, Map of</h2>
<p>Les interfaces <code>List</code>, <code>Set</code> et <code>Map</code> reçoivent des méthodes <code>of</code> (<a href="http://openjdk.java.net/jeps/269">JEP269</a>).
Ces méthodes retournent une <code>List</code>, un <code>Set</code> ou une <code>Map</code> <strong>immuables</strong>
contenant les objets passés en paramètres. Elles sont bien sûr génériques
(<em>generics</em>). Je peux par exemple écrire pour un <code>Set</code>:</p>
<pre><code class="language-java">
Set<String> s = Set.of("Vicky", "Jenny", "Karine");
</code></pre>
<p>alors que je devais écrire:</p>
<pre><code class="language-java">
Set<String> oldWay = new HashSet(
Arrays.asList("Vicky", "Jenny", "Karine"));
oldWay = Collections.unmodifiableSet(oldWay);
</code></pre>
<p>Pour une liste que je voudrais modifiable, je peux écrire;</p>
<pre><code class="language-java">
List<Integer> l = new ArrayList<>(
List.of(2,3,-5));
</code></pre>
<p>Je ne résiste pas à la tentation de vérifier<sup id="fnref:f1"><a href="https://blog.namok.be/?post/2018/03/22/JDK9#fn:f1" rel="footnote">1</a></sup> que ces méthodes statiques ont
bien été ajoutées dans l'interface <code>List</code>. Un <code>find</code> plus tard, je trouve le
fichier source que je peux éditer.</p>
<pre><code class="language-sh">
$ find . -name List.java
./src/java.desktop/java/awt/List.java
./src/java.xml.bind/com/sun/xml/internal/bind/v2/schemagen/
xmlschema/List.java
./src/jdk.compiler/com/sun/tools/javac/util/List.java
./src/java.base/java/util/List.java
$ gvim src/java.base/java/util/List.java
</code></pre>
<p>Extraits:</p>
<pre><code class="language-java">
public interface List<E> extends Collection<E> {
…
/**
* Returns an immutable list containing one element.
*
* See <a href="https://blog.namok.be/?post/2018/03/22/JDK9#immutable">Immutable List Static Factory
* Methods</a> for details.
*
* @param <E> the {@code List}'s element type
* @param e1 the single element
* @return a {@code List} containing the specified element
* @throws NullPointerException if the element is {@code null}
*
* @since 9
*/
static <E> List<E> of(E e1) {
return new ImmutableCollections.List1<>(e1);
}
</code></pre>
<h2 id="modules">Les modules, <em>Java Platform Module System (JPMS)</em> ou encore <em>Jigsaw</em></h2>
<p>Comme on l'a vu, Java découpe son code en modules — <em>Jigsaw</em> de petit nom
— dépendants les uns des autres. Cette découpe en modules — outre qu'elle
réorganise l'arborescence des sources — permet de structurer le JDK d'une part
et d'autoriser uniquement le chargement des modules nécessaires d'autre part.
Cette restructuration du <em>jdk</em> et du <em>jre</em> est sensée offrir de meilleures
performances, plus de sécurité et une maintenance plus aisée.<br />
Elle est décrite en partie dans <a href="http://openjdk.java.net/jeps/201">JEP201</a>, <a href="http://openjdk.java.net/jeps/261">JEP261</a> et <a href="http://openjdk.java.net/jeps/200">JEP200</a>.</p>
<p>Les fichiers java — et particulièrement le <em>bytecode</em> — sont réorganisées.
Toutes les classes standards ne se trouvent plus dans <code>rt.jar</code> et <code>tools.jar</code>…
qui ont disparus <a href="http://openjdk.java.net/jeps/220">JEP220</a>. Les classes se trouvent dans le répertoire <em>jmods</em>
contenant des fichiers au format <em>jmods</em>.</p>
<p>Le code se trouvant dans les modules et dans les fichiers <em>jar</em> traditionnels
basés sur le <em>classpath</em> peuvent coexister.</p>
<p>Cette notion de modules introduit:</p>
<ul>
<li><p>une sorte d'édition des liens — optionnelle — entre la phase de compilation et
celle d'exécution. Cette phase assemble les modules qui seront utilisés
à l'exécution grâce à <a href="https://docs.oracle.com/javase/9/tools/jlink.htm#JSWOR-GUID-CECAC52B-CFEE-46CB-8166-F17A8E9280E9">jlink</a>.</p></li>
<li><p>la notion de fichier <em>jar</em> modulaire qui est un fichier <em>jar</em> contenant un
fichier <code>module-info.class</code> à la racine. Ce fichier définit (voir plus bas)
l'organisation du module.</p></li>
<li><p>le format <em>jmod</em>, semblable au format <em>jar</em>, mais pouvant inclure du code natif
et des fichiers de configuration. Voir <a href="https://docs.oracle.com/javase/9/tools/jmod.htm#JSWOR-GUID-0A0BDFF6-BE34-461B-86EF-AAC9A555E2AE">jmod</a>.</p></li>
</ul>
<p>Pour définir un module, il est nécessaire d'ajouter un fichier
<code>module-info.java</code> à la racine du projet contenant:</p>
<pre><code class="language-java">
module org.example.my.module {
requires net.example.module.need;
export org.example.my.module.services;
}
</code></pre>
<p><strong>module</strong> définit le module, <strong>requires</strong> précise quels sont les modules
nécessaires et <strong>export</strong> présente publiquement les <em>packages</em> qui seront donc
visibles.</p>
<p>Voir par exemple <code>src/java.base/module-info.java</code><sup id="fnref:f2"><a href="https://blog.namok.be/?post/2018/03/22/JDK9#fn:f2" rel="footnote">2</a></sup>.</p>
<p>À <code>java</code> et <code>javac</code> s'ajoutent deux commandes:</p>
<ul>
<li><code>jlink</code> assemble des modules;</li>
<li><code>jdeps</code> informe sur les dépendances.</li>
</ul>
<h3>Exemple, <em>Hello, modular world</em></h3>
<p><a href="https://blog.namok.be/public/images/divers/2018/jdk9/jdk9-7-modular.png" title="jdk9-7-modular.png"><img src="https://blog.namok.be/public/images/divers/2018/jdk9/.jdk9-7-modular_s.png" alt="jdk9-7-modular.png" style="float: right; margin: 0 0 1em 1em;" title="jdk9-7-modular.png, mar. 2018" /></a></p>
<p>J'ai essayé d'utiliser Netbeans pour créer un module
avec un <em>new Java Modular Project</em>,
mais ça n'a pas été concluant.</p>
<p>Netbeans me crée bien un projet
auquel je peux ajouter un module. Il me crée alors un fichier <code>module-info.java</code>
que je peux compléter. Je crée un <em>package</em>, j'y place une classe et je clique
sur <em>"Run project…"</em>. Netbeans me demande de choisir une classe principale et
tout roule… sauf si je veux lancer le projet en dehors de Netbeans. La commande
qu'il me propose — et d'autres variantes — ne fonctionne pas.</p>
<p>Reprenons depuis le début en créant un projet tout à fait standard cette fois.</p>
<ol>
<li>Création d'un projet </li>
<li><p>Ajout d'un <em>package</em> contenant une classe <code>Hello</code> et ce code:</p>
<pre><code class="language-java">
package be.example.hello;
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, modular world");
}
}
</code></pre></li>
<li><p>Clic "Run" et ajout de la classe comme classe principale.</p></li>
<li><p>Ajout d'un fichier <code>module-info</code> via Netbeans. Il le crée en choisissant le
nom du module en fonction du nom du projet:</p>
<pre><code class="language-java">
module Jdk9Modular {
}
</code></pre></li>
<li><p>Ajout de la ligne</p>
<pre><code class="language-java">
exports be.example.hello;
</code></pre></li>
<li><p><em>clean and build</em> et Netbeans me propose une commande à exécuter.
Tout roule…</p>
<pre><code class="language-sh">
java
-p /elsewhere/jdk9-modular/dist/jdk9-modular.jar
-m Jdk9Modular
</code></pre></li>
</ol>
<p>Et si je voulais entrer les commandes « à la main » dans mon terminal.
Je me <a href="https://blog.codefx.org/java/java-module-system-tutorial/">base sur cette documentation</a> que j'adapte à mon projet Netbeans
et je suppose que je me trouve à la racine de mon projet.</p>
<pre><code class="language-sh">
javac -d build/classes
src/be.example.hello/classes/module-info.java
src/be.example.hello/classes/be/example/hello/Hello.java
jar --create
--file build/hello-modular-world.jar
--main-class be.example.hello.Hello
-C build/classes .
java
--module-path build/hello-modular-world.jar
--module be.example.hello
java
-p build/hello-modular-world.jar
-m be.example.hello
</code></pre>
<p>Les deux dernières commandes sont équivalentes. Notons qu'il n'est plus
nécessaire d'écrire un <em>manifest</em>, il suffit de renseigner la classe principale
avec <code>--main-class</code>.</p>
<h2 id="processus">Gestion des processus systèmes</h2>
<p>Une API dédiée à la gestion des processus et décrite dans <a href="http://openjdk.java.net/jeps/102">JEP102</a>. Avant
JDK9, il était possible de lancer des processus mais pas de les contrôler
ensuite.</p>
<p>Par exemple:</p>
<pre><code class="language-java">
Runtime.getRuntime().exec("/usr/bin/xeyes");
ProcessBuilder pb = new ProcessBuilder("/usr/bin/xeyes");
pb.start();
</code></pre>
<p><a href="https://blog.namok.be/public/images/divers/2018/jdk9/jdk9-2-processhandle.png" title="jdk9-2-processhandle.png"><img src="https://blog.namok.be/public/images/divers/2018/jdk9/.jdk9-2-processhandle_s.png" alt="jdk9-2-processhandle.png" style="float: right; margin: 0 0 1em 1em;" title="jdk9-2-processhandle.png, mar. 2018" /></a></p>
<p>La classe <code>ProcessHandle</code> offre moult méthodes pour contrôler un processus.
Ces méthodes lancent des <code>RuntimeException</code> ce qui est plus dans le mouvement
actuel par rapport aux exceptions contrôlées.</p>
<p>Je peux accéder au processus en cours mais également aux parents et aux enfants.
Si l'on compare à <code>ProcessBuilder</code> c'est beaucoup plus complet.</p>
<p>Puisque l'on a accès à une méthode <code>destroy</code> et à notre <code>pid</code>, essayons de nous
suicider. Je sais, c'est triste… et Java nous en empêche. Tout va bien ;-)</p>
<pre><code class="language-java">
ProcessHandle me = ProcessHandle.current();
System.out.printf("My process id: %d\n", me.pid());
System.out.printf("I'll try to kill myself (so sad)");
me.destroy();
</code></pre>
<p><a href="https://blog.namok.be/public/images/divers/2018/jdk9/jdk9-3-killmyself.png" title="jdk9-3-killmyself.png"><img src="https://blog.namok.be/public/images/divers/2018/jdk9/.jdk9-3-killmyself_m.png" alt="jdk9-3-killmyself.png" style="margin: 0 auto; display: block;" title="jdk9-3-killmyself.png, mar. 2018" /></a></p>
<h2 id="jshell">Jshell</h2>
<p><em>jShell</em> est une boucle REPL (<em>Read, Evaluate, Print, Loop</em>) proposant une sorte
de <em>shell</em> en Java (un peu à l'instar de Python). <em>jShell</em> propose une série de
commandes et une autocomplétion avec la touche [TAB]. Par exemple: <code>list</code> liste
les instructions entrées depuis le début de la session, <code>vars</code> liste les
variables déclarées, <code>history</code> pour l'historique, etc.</p>
<p>Extrait de l'aide:</p>
<pre><code>jshell> /help
| Type a Java language expression, statement, or declaration.
| Or type one of the following commands:
| /list [<name or id>|-all|-start]
| list the source you have typed
| /edit <name or id>
| edit a source entry referenced by name or id
| … <cut>
</code></pre>
<p>Pour quitter, c'est <em>/exit</em>. Voir ci-dessous pour un exemple de « séance
jShell ».</p>
<p><a href="https://blog.namok.be/public/images/divers/2018/jdk9/jdk9-4-jshell.png" title="jdk9-4-jshell.png"><img src="https://blog.namok.be/public/images/divers/2018/jdk9/.jdk9-4-jshell_m.png" alt="jdk9-4-jshell.png" style="margin: 0 auto; display: block;" title="jdk9-4-jshell.png, mar. 2018" /></a></p>
<p>Pour voir le contenu d'une variable, inutile d'écrire <code>System.out.print</code>, son
nom suffit:</p>
<pre><code class="language-jshell">
jshell> double var = 3.14;
var ==> 3.14
jshell> System.out.print
print( printf( println(
jshell> System.out.println(var);
3.14
jshell> var
var ==> 3.14
</code></pre>
<p>Pour utiliser une classe d'un <em>package</em> spécifique, il y a 3 manières de faire —
merci <a href="https://stackoverflow.com/questions/43111018/how-to-import-external-libraries-in-jshell-java-9/43112866">stacxoverflow</a>:</p>
<ol>
<li>mettre à jour son <em>CLASSPATH</em> avant de lancer <em>JShell</em>;</li>
<li><p>utiliser l'option idoine de la commande <em>jshell</em>;</p>
<pre><code class="language-jshell">
jshell --class-path beautifuljar.jar
</code></pre></li>
<li><p>utiliser la commande interne <em>/env</em></p>
<pre><code class="language-jshell">
/env -class-path beautifuljar.jar
</code></pre></li>
</ol>
<p><a href="https://blog.namok.be/public/images/divers/2018/jdk9/jdk9-8-jshell-import.png" title="jdk9-8-jshell-import.png"><img src="https://blog.namok.be/public/images/divers/2018/jdk9/.jdk9-8-jshell-import_m.png" alt="jdk9-8-jshell-import.png" style="margin: 0 auto; display: block;" title="jdk9-8-jshell-import.png, mai 2018" /></a></p>
<h2 id="stream">Nouvelles méthodes pour la classe Stream</h2>
<p>Quatre nouvelles méthodes pour <code>Stream</code>: <code>takeWhile</code>, <code>dropWhile</code>,
<code>ofNullable</code> et <code>iterate</code>.</p>
<p><em>takeWhile</em> — et c'est pareil pour <em>dropWhile</em> — est une méthode qui va
<strong>prendre</strong> les éléments <strong>tant que</strong> la condition est respectée et c'est en ça
qu'elle diffère de <em>filter</em> qui parcourt tout le flux.</p>
<p>Attention, si le flux n'est pas ordonné, <code>take|dropWhile</code>retourne n'importe quel
sous-ensemble correspondant à la condition… même si l'on peut supposer que le
développeur s'arrêtera dès qu'il aura trouvé un faux et ne retournera pas un
autre sous-ensemble du flux.</p>
<blockquote>
<p>If this stream is unordered, and some (but not all) elements of this stream
match the given predicate, then the behavior of this operation is
nondeterministic; it is free to take any subset of matching elements (which
includes the empty set).</p>
<p>Extrait de la Javadoc</p>
</blockquote>
<p>Par exemple:</p>
<pre><code class="language-java">
System.out.print("\nFilter ");
Stream.of(1,2,3,1,2,3,1,2,3)
.filter(i -> i<3)
.forEach(i -> System.out.printf("%d ", i)); // Filter 1 2 1 2 1 2
System.out.print("\nTake while ");
Stream.of(1,2,3,1,2,3,1,2,3)
.takeWhile(i -> i<3)
.forEach(i -> System.out.printf("%d ", i)); // Take while 1 2
</code></pre>
<p><em>iterate</em> voit apparaitre une nouvelle version avec un argument supplémentaire.
Là ou l'on utilisait <em>limit</em> comme par exemple:</p>
<pre><code class="language-java">
Stream.iterate(1, n -> n+1)
.limit(9)
.forEach(i -> System.out.printf("%d ", i));
</code></pre>
<p>on pourra directement mettre un <em>predicate</em> — bien plus général donc que <em>limit</em>
— en argument. Un peu comme:</p>
<pre><code class="language-java">
Stream.iterate(1, n -> n<10, n -> n+1)
.forEach(i -> System.out.printf("%d ", i));
</code></pre>
<p><em>ofNullable</em> retourne un <em>stream</em> d'un élément ou un <em>stream</em> vide dans le cas
d'un élément <em>null</em>. Ouais.</p>
<h2 id="javadoc">Javadoc</h2>
<p>Remise en forme légère au niveau graphique de la <em>javadoc</em>, passage à HTML5,
support des commentaires <em>javadoc</em> dans les déclarations de modules et
<strong>ajout d'une zone de recherche</strong>. Et ça s'est bien.</p>
<p><a href="https://blog.namok.be/public/images/divers/2018/jdk9/jdk9-5-javadoc.png" title="jdk9-5-javadoc.png"><img src="https://blog.namok.be/public/images/divers/2018/jdk9/.jdk9-5-javadoc_m.png" alt="jdk9-5-javadoc.png" style="margin: 0 auto; display: block;" title="jdk9-5-javadoc.png, mar. 2018" /></a></p>
<h2 id="underscore">_ underscore est un mot clé</h2>
<p>Le caractère <code>_</code> est devenu un <em>keyword</em> en java. Il ne peut plus être utilisé
comme un <em>identifier</em>.</p>
<h2 id="optional">La classe <em>Optional</em> se dote de nouvelles méthodes</h2>
<p>Quatre nouvelles méthodes également pour la classe <code>Optional</code>: <code>ifPresent</code>,
<code>ifPresentOrElse</code>, <code>or</code> et <code>stream</code>.</p>
<pre><code class="language-java">
/*
* If factory fail to give a Video, create new video.
* Silly example.
*/
Optional<Video> ov = Optional
.of(Videos.getRandomVideo())
.or(() -> Optional.of(
new Video("Beautiful Author", "My beautiful video")));
ov.ifPresent(v -> v.like());
</code></pre>
<h2 id="version">Numérotation des versions</h2>
<p>La numérotation des versions est revue — on abandonne définitivement le 1.x — et
suit le schéma suivant (<a href="http://openjdk.java.net/jeps/223">JEP223</a>):</p>
<pre><code class="language-sh">
$MAJOR.$MINOR.$SECURITY.$PATCH
</code></pre>
<h2 id="sha">Algorithmes de hashages supplémentaires</h2>
<p>Ajout des algorithmes <em>SHA3</em> (<a href="http://openjdk.java.net/jeps/283">JEP283</a>).</p>
<pre><code class="language-java">
StringJoiner sj = new StringJoiner(" ", "Algorithms: ", "\n");
Security.getAlgorithms("MessageDigest")
.forEach(s -> sj.add(s));
System.out.print(sj);
</code></pre>
<pre><code class="language-sh">
Algorithms: SHA3-512 SHA-384 SHA SHA3-384 SHA-224 SHA-512/256
SHA-256 MD2 SHA-512/224 SHA3-256 SHA-512 MD5 SHA3-224
</code></pre>
<p>Pour calculer un <em>hash</em>, on peut écrire le code suivant et vérifier que le
<em>hash</em> est le même que celui fourni par <code>echo -n "Beautifulmessage" | sha3sum
-a 512</code>:</p>
<pre><code class="language-java">
String message = "Beautifulmessage";
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA3-512");
md.update(message.getBytes());
byte[] bs = md.digest();
System.out.printf("Message: %s\n", message);
System.out.printf("Digest: ");
for (byte b : bs) {
System.out.printf("%02X", b);
}
System.out.println("");
} catch (NoSuchAlgorithmException ex) {
System.err.println("Algorithm error: " + ex.getMessage());
}
</code></pre>
<h2 id="string">Réduction de l'espace de stockage des chaines</h2>
<p>La représentation interne des chaines de caractères (<em>strings</em>) économise de
l'espace. Là où un <em>string</em> était stocké dans un tableau de <em>char</em> (2
<em>bytes</em> par caractère), il l'est maintenant dans un tableau de <em>byte</em> et un
attribut représentant l'encodage,<em>encoding-flag field</em> appelé <em>coder</em> (<a href="http://openjdk.java.net/jeps/254">JEP254</a>).</p>
<p>Les caractères constituants les chaines étaient codés en UTF-16, chaque caractère
occupant 1 <em>char</em> (parfois 2), soient 2 <em>bytes</em> (parfois 4). La plupart des
chaines de caractères ne contiennent que des caractères Latin-1 (ISO-8859-1). Un
caractère Latin-1 est codé sur 1 <em>byte</em>. La nouvelle classe <code>String</code> stocke les
caractères <strong>soit</strong> en Latin-1 (ISO-8859-1) <strong>soit</strong> en UTF-16 en fonction du
contenu de la chaine.</p>
<p>Extraits de code source de la classe <em>String</em> sans les commentaires:</p>
<pre><code class="language-java">
// JDK8
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
private int hash; // Default to 0
private static final long serialVersionUID =
-6849794470754667710L;
</code></pre>
<pre><code class="language-java">
// JDK9
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
private final byte[] value;
private final byte coder;
private int hash; // Default to 0
private static final long serialVersionUID =
-6849794470754667710L;
</code></pre>
<p>Java voit apparaitre deux nouvelles classes <code>StringUTF16</code> et <code>StringLatin1</code> et
la classe <code>String</code> se meuble d'instructions de style:</p>
<pre><code class="language-java">
if (isLatin1()) {
StringLatin1.foo();
} else {
StringUTF16.foo();
}
</code></pre>
<h2 id="unicode">Support d'unicode 8.0</h2>
<p>JDK8 supportait Unicode 6.2, JDK9 supporte Unicode 8.0 (<a href="http://openjdk.java.net/jeps/267">JEP267</a>). <em>That's all</em>.</p>
<h2 id="http2">http2</h2>
<p>Java passe d'une implémentation de HTTP/1.1 à HTTP/2 (<a href="http://openjdk.java.net/jeps/110">JEP110</a>). HTTP/1.1
posait quelques problèmes pour un <em>web</em> du XXI<sup>e</sup> siècle</p>
<ul>
<li><p>fins de lignes bloquants (<em>head of lines blocking</em> )</p>
<p>Les réponses du serveur sont reçues dans le même ordre qu'elles ont été
envoyées. Avec HTTP/2, les réponses peuvent être multiplexées. S'il faut
charger une grande page html contenant des images, il ne faudra plus
attendre le chargement complet de la page avant le chargement des images.</p></li>
<li><p>nombre de connexions réduit;</p>
<p><em>A single-user client SHOULD NOT maintain more than 2 connections with any
server or proxy. (<a href="https://tools.ietf.org/html/rfc2616">RFC2616</a>)</em></p></li>
<li><p>les <em>headers</em> sont toujours envoyés en texte. Avec HTTP/2, certains <em>headers</em>
seront envoyés en binaire plutôt qu'en texte quand l'efficacité le demande;</p></li>
</ul>
<p>HTTP/2 nous promet (voir <a href="https://www.mnot.net/blog/2014/01/30/http2_expectations">cette note de blog (en)</a>); la même API (<em>same
API</em>), des requêtes moins couteuses (<em>cheaper requests</em>), un réseau plus
convivial limitant les connexions (<em>network and server friendliness</em>), <em>cache
pushing</em> (le serveur est capable de fournir des données sans requêtes du
client), avec les connexions persistantes, le développeur pourra concevoir son
application différemment (<em>change your mind</em>) et plus de chiffrement (<em>more
encryption</em>).</p>
<p>L'API fournit trois classes; <code>HTTPClient</code>, <code>HTTPRequest</code> et <code>HTTPResponse</code>. Ces
classes permettent d'instancier un client, de formuler une requête et d'attendre
une réponse. Facile. Faire une requête d'une page se fait avec un code
à l'allure suivante (<a href="https://labs.consol.de/development/2017/03/14/getting-started-with-java9-httpclient.html">source</a>):</p>
<pre><code class="language-java">
try {
HttpClient client = HttpClient
.newBuilder()
.version(HttpClient.Version.HTTP_2)
.build();
HttpRequest request = HttpRequest
.newBuilder(new URI("http://pit.namok.be"))
.GET()
.build();
HttpResponse<String> response = client
.send(request, HttpResponse.BodyHandler.asString());
System.out.println(response.statusCode());
System.out.println(response.body());
} catch (<some exceptions> ex) {
ex.printStackTrace();
}
</code></pre>
<p>Les classes <code>HttpClient</code> et <code>HttpRequest</code> ont une méthode <code>newBuilder</code> acceptant
toute une série de paramètres (version…) qu'il suffit de chainer avant d'appeler
la méthode <code>build</code> qui construira l'objet.</p>
<p>Faire une requête <em>https</em> est aussi simple que l'ajout d'un <strong>s</strong> dans l'url.</p>
<p>Pour stocker le résultat de la requête dans un fichier il faudra le signaler via
le paramètre <em>BodyHandler</em>. <code>BodyHandler.asFile(…)</code>. Par exemple:</p>
<pre><code class="language-java">
try {
HttpClient client = HttpClient
.newBuilder()
.version(HttpClient.Version.HTTP_2)
.build();
HttpRequest request = HttpRequest
.newBuilder(new URI("https://pit.namok.be"))
.GET()
.build();
Path tempFile = Files.createTempFile("http2-test", ".html");
HttpResponse<Path> response = client
.send(request,
HttpResponse.BodyHandler.asFile(tempFile));
System.out.println(response.statusCode() + "\n"
+ response.body());
} catch (<some exceptions> ex) {
ex.printStackTrace();
}
</code></pre>
<p>Le dernier exemple montre comment faire une requête asynchrone cette fois.
En utilisant les <em>lambdas</em>, c'est impressionnant comme c'est facile à écrire.</p>
<pre><code class="language-java">
try {
HttpClient client = HttpClient
.newBuilder()
.version(HttpClient.Version.HTTP_2)
.build();
HttpRequest request = HttpRequest
.newBuilder(new URI("https://pit.namok.be"))
.GET()
.build();
Path tempFile = Files.createTempFile("http2-test", ".html");
CompletableFuture<HttpResponse<Path>> futureResponse = client
.sendAsync(request,
HttpResponse.BodyHandler.asFile(tempFile))
.orTimeout(2000, TimeUnit.MILLISECONDS)
.whenComplete((r, e)
-> System.out.printf("Callback status %d %s\n",
r.statusCode(),
r.body()))
.exceptionally((e) -> {
System.out.printf("Exceptionally %s\n",
e.getClass());
return null;
});
futureResponse.join();
} catch (IOException
| URISyntaxException ex) {
ex.printStackTrace();
}
</code></pre>
<p>Notez que le client HTTP est un « module incubateur » (<em>incubator module</em>). Ce
qui signifie que:</p>
<ul>
<li><p>le module est appelé <code>jdk.incubator.httpclient</code> et les classes
<code>jdk.incubator</code> <code>.http.Http*</code>. Ce module doit être ajouté par le biais d'un
fichier <code>module-info</code> qui aura la forme suivante:</p>
<pre><code class="language-java">
module pbt.trys {
requires jdk.incubator.httpclient;
}
</code></pre></li>
<li><p>les classes qui incubent ne se trouveront pas dans Java 10 et peuvent être
modifiées. Dans ce cas précis, le module s'appellera <code>java.httpclient</code> et les
classes (probablement) <code>java.http.Http*</code>.</p></li>
</ul>
<h2 id="inputstream">La classe <em>InputStream</em> se dote également de nouvelles méthodes</h2>
<p>Trois nouvelles méthodes utilitaires pour la classe <code>InputStream</code>:
<code>readAllBytes</code>, <code>readNBytes</code> et <code>transferTo</code>.</p>
<p>Ces méthodes appellent peu de commentaires. Les deux premières permettent la lecture
d'un ficher dans un tableau de <em>bytes</em> et sont plutôt destinées aux petits
fichiers et la dernière sert à envoyer directement le flux d'entrée vers un flux
de sortie.</p>
<h2 id="flow">La classe <code>Flow</code>, le producteur et le consommateur</h2>
<p>Ce <em>design pattern</em> est très simple; un producteur qui produit des tâches, un
consommateur qui exécute les tâches une par une et une file d'attente dans
laquelle le producteur ajoute ses tâches. Le consommateur les retire une par
une. Il peut bien sûr y avoir plusieurs producteurs et plusieurs consommateurs.</p>
<p>C'est la gestion de la file d'attente qui doit être soignée. Elle
est en forte concurrence d'accès. Actuellement, si je devais coder ce <em>design</em>,
je mettrais l'accès à la file d'attente en section critique (via un
<em>synchronized</em>, ou en utilisant une <code>BlockingQueue</code>).</p>
<p>Java 9 introduit une nouvelle classe <code>Flow</code> dans le <em>package</em>
<code>java.util.concurrent</code> pour une mise en œuvre de ce <em>pattern</em> <strong>producteur
/ consommateur</strong>. Cette classe <code>Flow</code> propose trois interfaces;
<code>Flow.Publisher<T></code>, <code>Flow.Subscriber<T></code> et <code>Flow.Subscription</code></p>
<p>J'écris une classe implémentant <code>Subscriber<T></code> pour le consommateur. Cette
interface contient quatre méthodes assez naturelles; une pour s'enregistrer, une
pour faire le boulot, une pour gérer les erreurs et la dernière pour clôturer.
Je peux écrire un code à l'allure suivante:</p>
<pre><code class="language-java">
public class VideoSubscriber implements Subscriber<Video> {
private Subscription subscription;
@Override
public void onSubscribe(Subscription subscription) {
this.subscription = subscription;
this.subscription.request(1);
}
@Override
public void onNext(Video v) {
System.out.println("I publish " + v.getTitle()
+ " from " + v.getAuthor());
v.publish();
subscription.request(1);
}
@Override
public void onError(Throwable arg0) {
System.err.printf("Error (%s)\n", arg0.getMessage());
System.exit(1);
}
@Override
public void onComplete() {
System.out.printf("Done\n");
}
}
</code></pre>
<p>Le producteur doit implémenter la classe <code>Publisher<T></code>. Il existe déjà une
classe l'implémentant. Il s'agit de <code>SubmissionPublisher<T></code>. Cette classe
propose — entre autres — une méthode <code>submit</code> qui soumet une tâche. Je peux donc
directement écrire un <em>main</em> à l'allure suivante:</p>
<pre><code class="language-java">
SubmissionPublisher<Video> publisher = new SubmissionPublisher<>();
VideoSubscriber subscriber = new VideoSubscriber();
publisher.subscribe(subscriber);
Videos.getRandomVideos(12).stream()
.forEach(v -> publisher.submit(v));
// wait consummer end
try {
Thread.sleep(5000);
System.exit(0);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
publisher.close();
</code></pre>
<p><em>Teaser</em> Java 10. Je pourrai bientôt écrire:</p>
<pre><code class="language-java">
var publisher = new SubmissionPublisher<Video>();
var subscriber = new VideoSubscriber();
</code></pre>
<p>… mais chut :-)</p>
<p>En attendant, essayez d'ajouter des consommateurs et des producteurs. Toute la
gestion est faite par Java.</p>
<h2 id="more">Quelques points non détaillés</h2>
<ol>
<li><p><em>Multi-release jar files</em> (<a href="http://openjdk.java.net/jeps/238">JEP238</a>);</p>
<p>Complète la structure des fichiers jar et autorise plusieurs <em>releases</em>
différentes pour une classe. Par exemple JDK9 et <JDK9.</p></li>
<li><p>annotation <code>@Deprecated</code>;</p>
<p>L'annotation se complète de paramètres. Par exemple la classe
<code>java.lang.Compiler</code></p>
<pre><code class="language-java">
package java.lang;
/**
* The {@code Compiler} class is provided to support
* Java-to-native-code compilers and related services.
* By design, the {@code Compiler} class does
* …
*/
@Deprecated(since="9", forRemoval=true)
public final class Compiler {
…
}
</code></pre></li>
<li><p>le moteur de rendu JavaFX et Java 2D est <em>Marlin</em> et plus <em>Pisces</em> ni
<em>Ductus</em> (<a href="http://openjdk.java.net/jeps/265">JEP265</a>);</p></li>
<li><p>le <em>garbage collector</em> (ramasse-miettes) sera G1 et plus <em>Parallel GC</em>
(<a href="http://openjdk.java.net/jeps/248">JEP248</a>);</p></li>
<li><p>à noter que l'on peut écrire des méthodes privées dans les interfaces… utiles
aux <em>default methods</em> (<a href="http://openjdk.java.net/jeps/213">JEP213</a>);</p></li>
<li><p>l'API <code>Applet</code> est dépréciée <em>@Deprecated(since = "9")</em> (<a href="http://openjdk.java.net/jeps/289">JEP289</a>);</p></li>
<li><p>suppression de certains outils obsolètes ou ajoutés à l'époque comme simples
outils de démonstration; <em>hprof</em>, <em>jhat</em> (<a href="http://openjdk.java.net/jeps/240">JEP240</a>, <a href="http://openjdk.java.net/jeps/241">JEP241</a>);</p></li>
</ol>
<p></br></p>
<p>Pour aller plus loin, vous pouvez consulter les
<a href="https://docs.oracle.com/javase/9/whatsnew/toc.htm">changements complets et classés chez Oracle</a>. Et reprendre ensuite une activité normale.</p>
<p></br></p>
<p></br></p>
<p>Crédit photo chez <a href="http://deviantart.com">DeviantArt</a> par <a href="https://pixelduster.deviantart.com/art/Unzipped-Egg-33169606">Pixel duster</a>. Un « neuf » pour Java 9. Bon d'accord.</p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:f1">
<p>Je ne vérifie pas à proprement parler hein ! J'illustre la curiosité qui
permet de comprendre les choses et qui montre que rien n'est magique et que
toutes les briques se mettent bien en place. Vous pouvez faire pareil. <a href="https://blog.namok.be/?post/2018/03/22/JDK9#fnref:f1" rev="footnote">↩</a></p>
</li>
<li id="fn:f2">
<p>Comme vous avez déjà décompressé les sources… <a href="https://blog.namok.be/?post/2018/03/22/JDK9#fnref:f2" rev="footnote">↩</a></p>
</li>
</ol>
</div>
Grammalecte, plugin vimurn:md5:603a149edd178f6da7ce38f8bd566da32018-03-13T15:11:00+01:002018-03-13T15:14:29+01:00PiTMes doigts dans le clavieralternativesgeekvim <p><a href="https://www.dicollecte.org/">Grammalecte</a> est un correcteur orthographique <strong>et</strong> grammatical.
Il est disponible pour <em>LibreOffice</em> et <em>Thunderbird</em> par exemple mais aussi
pour <strong>vim</strong> via un <a href="https://github.com/dpelle/vim-Grammalecte">plugin</a>.</p>
<p>Pour l'installer et j'alouter à <em>vim</em>, <a href="http://namok.be/blog/?post/2014/03/14/howto-vim">je suppose que pathogen est déjà
installé et utilisé</a>, voici quelques étapes:</p>
<ol>
<li><p>télécharger et installer <strong>grammalecte client</strong> <a href="https://www.dicollecte.org/?download_div">download</a>;</p>
<p>Personnellement, je dézippe dans <code>/opt/grammalecte</code>.</p></li>
<li><p>cloner le <em>dépot</em> <em>git</em> dans le répertoire <em>pathogen</em>;</p>
<pre><code class="language-sh">
cd ~/.vim/bundle
git clone https://github.com/dpelle/vim-Grammalecte
</code></pre></li>
<li><p>ajouter le chemin vers <em>grammalecte</em> dans le fichier de configuration de
<em>vim</em> de l'utilisateur (<code>~/.vim/vimrc</code>);</p>
<pre><code class="language-vi">
" for grammalecte
let g:grammalecte_cli_py='/opt/grammalecte/grammalecte-cli.py'
</code></pre></li>
</ol>
<p>C'est tout pour l'installation. Le <em>plugin</em> vim <em>Grammalecte</em> est fourni avec
deux commandes — <em>GrammalecteCheck</em> et <em>GrammalecteClear</em> — que <a href="http://namok.be/blog/?post/2014/03/14/howto-vim">je <em>mappe</em> sur
ma <em>leader key</em></a>.</p>
<ol>
<li><p>ajout d'un <em>mapping</em></p>
<pre><code class="language-">
nnoremap <leader>gl :GrammalecteCheck<cr>
nnoremap <leader>gc :GrammalecteClear<cr>
</code></pre></li>
</ol>
<p>…pour le reste on verra à l'usage.</p>
Une manière de mettre en place un certificat Letsencrypt avec Certbot sans les privilèges de rooturn:md5:a2ffd8e95f5e674e1bb6e906f6e6627c2018-03-01T11:44:00+01:002018-05-22T13:57:13+02:00PiTMes doigts dans le clavierdebiangeekinternet<p><img src="https://blog.namok.be/public/images/divers/2018/358H-web.jpg" alt="358H-web.jpg" style="margin: 0 auto; display: block;" title="358H-web.jpg, mar. 2018" /></p>
<p><em>La rédaction de ce billet a débuté en mai 2017… j'ai un peu trainé.</em></p>
<p>C'est dans l'air du temps de passer du web (<em>world wide web</em>) non chiffré au
web chiffré. Pour passer de <code>http</code> à <code>https</code>, il faut mettre en place un
échange de clés cryptographiques entre le client (le navigateur) et le serveur
(le site web visité). Cet échange de clé doit être certifié par une autorité
de… certification (voir <a href="https://fr.wikipedia.org/wiki/Autorit%C3%A9_de_certification">Wikipedia</a> pour les détails).</p>
<p>Pour vérifier que les intervenants sont bien qui ils prétendent être,
l'autorité de certification fait certaines vérifications. Les plus
élémentaires sont de voir si le demandeur à accès, au choix, à l'hébergement
web, aux mails ou à la zone DNS.</p>
<p>Cette vérification à un coût. La vérification la plus simple coûte ±12€/an et
peut aller <a href="https://www.comodo.com/">jusqu'à 200€ voire plus</a>. <a href="https://letsencrypt.org/">Letsencrypt</a> est
une société qui désire permettre à qui veut de mettre en place un certificat
ssl — son site en <code>https</code> — gratuitement et simplement.</p>
<p><em>https à la portée de tous</em></p>
<p>La contrainte de <em>letsencrypt</em> est que le certificat doit être renouvelé tous
les 90 jours alors que les certificats payants sont généralement valables au
minimum 1 an. Ce n'est pas bien grave car le renouvellement peut être
automatisé.</p>
<p>Quand j'ai commencé à rédiger ce billet, <em>letsencrypt</em> était moyennement connu.
Aujourd'hui — presque un an plus tard parce que certains billets trainent
— beaucoup d'hébergeurs proposent <em>https</em> directement grâce à <em>letsencrypt</em>.
Même si l'on n'a plus aucune excuse pour ne pas proposer <em>https</em>, l'on peut
quand même se poser la question de son utilité pour une page ne contenant aucun
formulaire… mais c'est une autre histoire.</p>
<p>La force de <em>letsencrypt</em> — en plus du fait que ce soit gratuit — est de fournir
un programme — officiel car il en existe plein d'autres — permettant
d'automatiser le travail. Il est possible d'utiliser <strong><a href="https://certbot.eff.org/#debianjessie-apache">certbot</a></strong> et — en lui
donnant les droits <em>root</em> — de lui demander de faire (quasi) tout le boulot.
Voir <a href="https://www.grafikart.fr/formations/serveur-linux/apache-ssl-letsencrypt">ce tutorial</a> par exemple. C'est très simple.</p>
<p>Si pour diverses raisons vous ne voulez pas lancer <em>certbot</em> en root ou utiliser
d'autres outils comme <strong><a href="https://github.com/srvrco/getssl">getssl</a></strong> ou <strong><a href="https://github.com/lukas2511/dehydrated">dehydrated</a></strong>, il est
possible de faire autrement.</p>
<p>J'ai choisi de faire la demande de certificat sur ma machine locale et de
tout mettre dans un même répertoire. Ensuite je copie les deux fichiers <em>qui
vont bien</em> sur mon serveur. J'ai évidemment fait la configuration du serveur
web — <strong>apache</strong> — manuellement.</p>
<p>C'est cette approche sans doute particulière que je présente ici.<br />
Voici l'allure de la manipulation.</p>
<pre><code class="language-">
apt install certbot
</code></pre>
<p>Génération des certificats de manière <strong>manuelle</strong> et en utilisant la
vérification <strong>dns</strong>. Imaginons que je travaille dans le répertoire
<code>~/elsewhere/ssl</code>. Je demande à <em>certbot</em> de tout mettre dans le répertoire
courant.</p>
<pre><code class="language-bash">
certbot certonly --manual \
--preferred-challenges dns \
-d elsewhere.example.org \
--config-dir . --logs-dir . --work-dir .
</code></pre>
<p><em>certbot</em> me demande mon email et me demande de placer un enregistrement DNS
dans la zone concernée. Je m'exécute.</p>
<pre><code class="language-bash">
_acme-challenge 10800 IN TXT "UPailéchalotte52xawazaa_Qu4HV81bkBim\
paf-Plop-I"
</code></pre>
<p>Dès que c'est fait, il me génère tout le merdier qui a l'allure suivante:</p>
<p><img src="https://blog.namok.be/?post/2018/03/01/BLOG-letsencrypt-1.png" alt="Commande tree dans le répertoire courant" /></p>
<p>Les deux fichiers — que je renommerai — à transférer sur le serveur web sont:</p>
<ul>
<li><code>live/privkey.pem</code> comme <code>SSLCertificateKeyFile</code> et </li>
<li><code>live/fullchain.pem</code> comme <code>SSLCertificateFile</code></li>
</ul>
<p>J'ajoute une configuration <em>apache</em> à l'allure suivante:</p>
<pre><code class="language-">
<IfModule mod_ssl.c>
<VirtualHost _default_:443>
ServerAdmin webmaster@elsewhere.example.org
ServeName elsewhere.example.org
DocumentRoot /var/www/html/elsewhere.example.org/
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
SSLEngine on
SSLCertificateFile
/etc/apache2/ssl/elsewhere.example.org.fullchain.pem
SSLCertificateKeyFile
/etc/apache2/ssl/elsewhere.example.org.privkey.pem
</VirtualHost>
</IfModule>
</code></pre>
<p>… et tout roule.</p>
<p>Il reste à prévoir le renouvellement du certificat lors de la réception du mail
de <em>letsencrypt</em>. C'est mieux si ce renouvellement est un peu automatisé.</p>
<p>Comme la commande est lancée manuellement, pas de <em>renew</em> possible, il faut
relancer la commande initiale. Nous allons l'adapter un peu pour notre facilité.</p>
<ul>
<li><p>pour le nenouvellement, il est nécéssaire d'actualiser le <em>challenge</em>.</p>
<p>Dans notre cas, il faudrait adapter l'enregistrement dns ce qui nécessite
une connexion chez l'hébergeur ou un client spécifique (<a href="https://id-rsa.pub/post/certbot-auto-dns-validation-with-lexicon/">exemple avec
lexicon</a>). Le challenge <em>http</em> demande qu'un fichier soit présent sur
l'hébergement… ce qui peut être fait via <em>scp</em>. C'est plus facile et ça peut
être automatisé.</p></li>
<li><p><em>certbot</em> a un <a href="https://certbot.eff.org/docs/using.html#pre-and-post-validation-hooks">système de <em>hooks</em></a> qui peuvent être déclenchés avant la
vérification et après.</p>
<p>Dès que <em>letsencrypt</em> nous communique notre challenge — que ce soit
l'enregistrement dns à placer ou le fichier à déposer — c'est le <em>hook</em> qui
se chargera de la faire.</p></li>
</ul>
<p>J'ajoute deux scripts à l'allure suivante:</p>
<ul>
<li><p>le premier sera exécuté avant et va donc déposer le challenge</p>
<pre><code class="language-bash">
$ cat before.sh
#! /usr/bin/env bash
echo $CERTBOT_VALIDATION > /tmp/$CERTBOT_TOKEN
scp /tmp/$CERTBOT_TOKEN \
user@host.example.org:\
/var/www/elsewhere.example.org/.well-known/acme-challenge
rm /tmp/$CERTBOT_TOKEN
</code></pre></li>
</ul>
<p></p>
<ul>
<li><p>le second ira déposer les certificats sur le serveur<sup id="fnref:f1"><a href="https://blog.namok.be/?post/2018/03/01/letsencrypt-certbot-sans-root#fn:f1" rel="footnote">1</a></sup></p>
<pre><code class="language-bash">
$ cat after.sh
#! /usr/bin/env bash
scp live/elsewhere.example.org/privkey.pem \
user@host.examble.org:/home/user/ssl/elsewhere.example.org.key.pem
scp live/elsewhere.example.org/fullchain.pem \
user@host.examble.org:
/home/user/ssl/elsewhere.example.org.fullchain.pem
</code></pre></li>
</ul>
<p>Lors du renouvellement, j'entre alors une commande à l'allure suivante (le
paramètre <em>manual-public-logging-ok</em> évite le prompt Y/N pour le <em>log</em> de l'IP
et <em>max-log-backups</em> limite le nombre de logs à 3… au lieu de 1000):</p>
<pre><code class="language-bash">
certbot certonly --manual \
--preferred-challenges http \
-d elsewhere.example.org \
--config-dir . --logs-dir . --work-dir . \
--manual-public-ip-logging-ok \
--manual-auth-hook ../before.sh \
--manual-cleanup-hook ../after.sh \
--max-log-backups 3
</code></pre>
<p>… et tout est fait… presque car il faut encore faire un <em>reload</em> du serveur
apache.</p>
<p>Cette méthode a ses défauts, elle permet de comprendre le fonctionnement de
<em>letsencrypt</em> et de garder la main sur toute la chaine. C'est pédagogique ;-)</p>
<p>Enjoy !</p>
<p><br/></p>
<p><em>Crédit photo chez <a href="https://gratisography.com/">Gratisography</a>. Sérieux avec un grain de
folie.</em></p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:f1">
<p>Si tu es attentif, tu vois que je ne copie pas dans <code>/etc/apache2</code> mais
dans le home de l'utilisateur… les fichiers dans <code>/etc/apache2</code> sont des liens
<em>softs</em>. Ceci permet de ne pas se connecter en <em>ssh</em> (via <em>scp</em>) avec les droits
<em>root</em>. <a href="https://blog.namok.be/?post/2018/03/01/letsencrypt-certbot-sans-root#fnref:f1" rev="footnote">↩</a></p>
</li>
</ol>
</div>
Apache derby, SQL interpréteururn:md5:e8eb193ebd20c85b5de1fd66d69eea0f2018-01-31T23:12:00+01:002018-02-13T08:12:10+01:00PiTMes doigts dans le clavieresigeekjava<p><a href="https://blog.namok.be/public/images/divers/2018/bois-beloeil.jpg"><img src="https://blog.namok.be/public/images/divers/2018/bois-beloeil.jpg" alt="" style="margin: 0 auto; display: block;" /></a></p>
<p><strong>Apache derby</strong> est un SGBD (Système de gestion de base de données) <em>open
source</em> implémenté en Java et disponible sous les termes de la licence Apache
v2.0.</p>
<p>Voici quelques avantages — simplement traduits du <a href="http://db.apache.org/derby/index.html">site officiel du projet</a>:</p>
<ul>
<li><p><em>derby</em> a un légère empreinte de l'ordre de 3,5Mib pour le moteur de base et
le <em>driver</em> JDBC embarqué;</p></li>
<li><p><em>derby</em> est basé sur Java, JDBC et les standards SQL;</p></li>
<li><p><em>derby</em> fournit un <em>driver</em> JDBC embarqué qui vous permet d'embarquer <em>derby</em>
dans n'importe quel solution Java;</p></li>
<li><p><em>derby</em> supporte également le modèle client/serveur habituel avec le driver
<em>Derby Network Client JDBC</em> et <em>Derby Network Server</em>;</p></li>
<li><p><em>derby</em> est facile à installer, déployer et utiliser.</p></li>
</ul>
<p>Jusque là, rien de bien neuf. Toute personne ayant développé en Java a utilisé
<em>derby</em>. Écrire du code utilisant JDBC et <em>derby</em> est assez simple. Ce n'est
pas l'objet de ce <em>post</em>. Une fois une base de données <em>derby</em> créée et
embarquée dans un projet, je voudrais l'interroger sans écrire du code Java.
Je voudrais une application dans mon terminal, comme <em>mysql</em> ou <em>psql</em>.</p>
<p><strong>ij</strong> est un outil permettant les requêtes SQL fourni avec <em>derby</em> et <a href="http://db.apache.org/derby/papers/DerbyTut/ij_intro.html">présenté
sur le site d'Apache</a>. Pour l'utiliser, il suffit de récupérer les <em>jars
qui vont bien</em> (<a href="http://db.apache.org/derby/derby_downloads.html">download</a>):</p>
<ul>
<li><code>derby.jar</code> pour le driver (par exemple le driver <em>embedded</em>). Mais ça,
c'est assez habituel et;</li>
<li><code>derbytools.jar</code> pour la classe <code>ij</code>.</li>
</ul>
<p>Après l'installation de <em>ij</em>, les <em>jars</em> se trouvent dans le sous-répertoire de
l'installation. Il suffit de les intégrer à sa variable d'environnement
<code>CLASSPATH</code>.</p>
<pre class="brush: bash">
$ export CLASSPATH=$CLASSPATH:/opt/db-derby/lib/derbytools.jar
:/opt/db-derby/lib/derby.jar
:/opt/db-derby/lib/derbyrun.jar
</pre>
<p>L'interpréteur SQL est une simple classe java. Une fois lancée, on obtient un
interpréteur SQL.</p>
<pre class="brush: bash">
$ java org.apache.derby.tools.ij
Version IJ 10.14
ij>
</pre>
<p><a href="http://db.apache.org/derby/papers/DerbyTut/ij_intro.html">Le tutoriel d'introduction fourni par Apache</a> se résume en quelques
commandes.</p>
<pre class="brush: bash">
ij> connect 'jdbc:derby:mydb;create=true';
ij> connect 'jdbc:derby:mydb';
</pre>
<p>Ces deux commandes se connectent à la bd. La première la créée si elle
n'existe pas tandis que la seconde suppose que la bd existe (dans le
répertoire courant).</p>
<p>Une fois connecté, je peux entrer des commandes SQL habituelles. Supposons
l'existence d'un fichier <code>my.sql</code> dont voici le contenu:</p>
<pre class="brush: sql">
create table MY
(
id DECIMAL(5) not null primary key,
digits VARCHAR(32),
digitd DECIMAL(1)
);
INSERT INTO MY VALUES(1,'zéro',0);
INSERT INTO MY VALUES(2,'un',1);
INSERT INTO MY VALUES(3,'deux',2);
INSERT INTO MY VALUES(4,'trois',3);
INSERT INTO MY VALUES(5,'quatre',4);
INSERT INTO MY VALUES(6,'cinq',5);
INSERT INTO MY VALUES(7,'six',6);
INSERT INTO MY VALUES(8,'sept',7);
INSERT INTO MY VALUES(9,'huit',8);
INSERT INTO MY VALUES(10,'neuf',9);
</pre>
<p>Supposons éqalement qu'aucune bd n'existe, une session pourrait ressembler à:</p>
<pre class="brush: bash">
ij> connect 'jdbc:derby:mydb;create=true';
ij> run 'my.sql';
ij> create table MY
(
id DECIMAL(5) not null primary key,
digits VARCHAR(32),
digitd DECIMAL(1)
);
0 lignes insérées/mises à jour/supprimées
ij> INSERT INTO MY VALUES(1,'zéro',0);
1 ligne insérée/mise à jour/supprimée
ij> INSERT INTO MY VALUES(2,'un',1);
1 ligne insérée/mise à jour/supprimée
ij> INSERT INTO MY VALUES(3,'deux',2);
1 ligne insérée/mise à jour/supprimée
ij> INSERT INTO MY VALUES(4,'trois',3);
1 ligne insérée/mise à jour/supprimée
ij> INSERT INTO MY VALUES(5,'quatre',4);
1 ligne insérée/mise à jour/supprimée
ij> INSERT INTO MY VALUES(6,'cinq',5);
1 ligne insérée/mise à jour/supprimée
ij> INSERT INTO MY VALUES(7,'six',6);
1 ligne insérée/mise à jour/supprimée
ij> INSERT INTO MY VALUES(8,'sept',7);
1 ligne insérée/mise à jour/supprimée
ij> INSERT INTO MY VALUES(9,'huit',8);
1 ligne insérée/mise à jour/supprimée
ij> INSERT INTO MY VALUES(10,'neuf',9);
1 ligne insérée/mise à jour/supprimée
ij> select * from my;
ID |DIGITS |DIG&
--------------------------------------------
1 |zéro |0
2 |un |1
3 |deux |2
4 |trois |3
5 |quatre |4
6 |cinq |5
7 |six |6
8 |sept |7
9 |huit |8
10 |neuf |9
10 lignes sélectionnées
ij> exit;
</pre>
<p><em>That's all folks</em>. Voilà pour la base.</p>
<p><br/></p>
<p><em>Crédit photo personnelle.</em></p>
Installation de cachet, the open source status page systemurn:md5:c575078f1fc648cee85491e5b5835afc2017-11-09T11:20:00+01:002017-11-09T11:43:46+01:00PiTMes doigts dans le clavierdebianesigeekgitlogiciellibre<p><img src="https://blog.namok.be/public/images/divers/2017/jonathan-perez-409943-1200x.jpg" alt="jonathan-perez-409943-1200x.jpg" style="margin: 0 auto; display: block;" title="jonathan-perez-409943-1200x.jpg, nov. 2017" /></p>
<p><strong>Cachet</strong> est un système qui offre une page de statut de différents services
web. Il permet de reporter rapidement et de centraliser l'état des services
web d'une société / asbl / groupement…</p>
<p>Pour l'école, ça se trouve là:
<a href="http://status.esi-bru.be">http://status.esi-bru.be</a></p>
<p>L'installation de <strong>cachet</strong> se fait en suivant <a href="https://docs.cachethq.io/docs/installing-cachet">la documentation
officielle</a>… à quelques changements près.</p>
<p>Pour le début, je suis simplement le tuto.</p>
<pre>
cd /var/www/html
git clone https://github.com/cachethq/Cachet.git
cd Cachet
git tag -l
git checkout v2.3.13
cp .env.example .env
vim .env
</pre>
<p>Édition du fichier de configuration. Comme ce sera une BD <em>postgresql</em>, il
faut — l'installer si ce n'est fait — créer la BD et créer un utilisateur
dédié: <em>cachet</em>. Pour ce faire:</p>
<ol>
<li><p>création de la BD</p>
<pre>
sudo -u postgres createdb cachet
</pre></li>
<li><p>choisir un utilisateur</p>
<pre>
sudo -u postges createuser --interactive
</pre></li>
<li><p>lui attribuer un mot de passe — <code>pwgen 10 -1</code> fait bien l'affaire — que je peux reporter dans le fichier de configuration</p>
<p></p>
<pre>
psql (9.6.3)
Saisissez « help » pour l'aide.
postgres=# \password cachet
Saisissez le nouveau mot de passe :
Saisissez-le à nouveau :
postgres=# \q
</pre></li>
</ol>
<p>Pour l'installation de <em>composer</em>, je préfère faire confiance à mon
gestionnaire de paquets et faire un <code>apt install composer</code> disponible
depuis <em>stretch</em> sous <em>debian</em>. La génération de la clé nécessite que
<em>composer</em> soit installé. Ensuite, l'installation de <em>cachet</em> se passe sans
soucis.</p>
<pre>
composer install
php artisan key:generate
php artisan app:install
</pre>
<p>Il est temps d'ajouter le <em>vhost</em> <em>apache2</em> — ou <em>nginx</em> — et d'activer <em>mod
rewrite</em>.</p>
<pre>
a2enmod rewrite
</pre>
<pre>
<VirtualHost *:80>
ServerName status.example.org
ServerAlias www.status.example.org
ServerAdmin webmaster@example.org
DocumentRoot /var/www/html/cachet/public
<Directory "/var/www/html/cachet/public">
Require all granted
# Used by Apache 2.4
Options Indexes FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
</pre>
<p>À ce stade, <em>cachet</em> est installé… mais n'a que peu d'interêt. Il devient
utile s'il est capable de se mettre à jour sans intervention humaine. Après
lecture de <a href="https://docs.cachethq.io/reference">la documentation</a>, je comprends que <em>cachet</em> propose une API
et que je peux le mettre à jour via… un script <em>python</em> par exemple.</p>
<pre>
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
#
import requests
import json
url= "http://status.example.org/api/v1/ping"
response = requests.request("GET", url)
parsed = json.loads(response.text)
print json.dumps(parsed)
</pre>
<p>Pour aider à écrire les requêtes, j'utilise la librairie
<a href="https://github.com/dmsimard/python-cachetclient">python-cachetclient</a> qui s'installe par une des deux commandes suivantes:</p>
<pre>
pip3 install python-cachetclient
pip3 install git+https://github.com/dmsimard/python-cachetclient
</pre>
<p>Les <a href="https://github.com/dmsimard/python-cachetclient/blob/master/contrib/example.py">examples</a> sont clairs et j'utilise — comme version 1 — le script
à l'allure suivante:</p>
<pre>
#!/usr/bin/env python3
#
# Python script for status.example.org
#
# Author: Pierre BT
# Thanks to cahethq and python-cachetclient
#
import cachetclient.cachet as cachet
import json
import requests
ENDPOINT = 'http://status.example.org/api/v1'
API_TOKEN = '140if12345ABCDefghijkl'
SATUS_OK = 1
SATUS_PERF_ISSUES = 2
SATUS_PARTIAL_OUTAGE = 3
INCIDENT_INVESTIGATING = 1
INCIDENT_IDENTIFIED = 2
INCIDENT_WATCHING = 3
INCIDENT_FIXED = 4
SITEWEB = 'http://example.org'
SITEWEB_ID = 1
def ping_site_web(site, components_id, components, incidents):
""" Ping site web and update status
"""
try:
request = requests.get(site)
if request.status_code == 200:
components.put(id=components_id, status=SATUS_OK)
else:
components.put(id=components_id, status=SATUS_PERF_ISSUES)
incidents.post(
name='Erreur ' + str(request.status_code)
+ ' sur ' + site,
message=('Tout le monde est sur le pont pour régler ça…'
'et ce message est automatique.'),
status=INCIDENT_INVESTIGATING)
except Exception as e:
components.put(id=SITEWEB_ID, status=SATUS_PARTIAL_OUTAGE)
incidents.post(
name='Problème grave sur le ' + site,
message=('Tout le monde est sur le pont pour régler ça… '
'et ce message est automatique.'),
status=INCIDENT_INVESTIGATING)
#
# main method
#
components = cachet.Components(endpoint=ENDPOINT, api_token=API_TOKEN)
incidents = cachet.Incidents(endpoint=ENDPOINT, api_token=API_TOKEN)
ping_site_web(SITEWEB, SITEWEB_ID, components, incidents)
</pre>
<p>Il reste à ajouter ce script dans une tache <strong>cron</strong> et la première
automatisation — certes assez basique — est en place.</p>
<p>Enjoy et merci aux <em>devs</em> !</p>
<p><br/></p>
<p><em>Crédit photo chez <a href="http://unsplash.com">Unsplash</a>par <a href="https://unsplash.com/photos/6BzGt4OXzeM">Jonathan Perez</a>. Les
grincheux·ses pourraient dire que c'est facile d'utiliser une photo d'un
boite de cachets… mais ce sont des grincheux·ses non ?</em></p>
Signal-desktop, la version desktop de Signalurn:md5:1319614aebc2478748e881d48d3e7e4a2017-11-01T16:14:00+01:002017-11-02T12:25:36+01:00PiTMes doigts dans le clavieralternativespratiquesecurity<p><img src="https://blog.namok.be/public/images/divers/2017/439-1200x.jpg" alt="439-1200x.jpg" style="margin: 0 auto; display: block;" title="439-1200x.jpg, nov. 2017" /></p>
<p>L'application <strong>Signal</strong> est une application de messagerie. Cette application
fournit le protocole éponyme à <em>Whatsapp</em> et <em>Facebook messenger</em>. Parmis
<a href="http://namok.be/blog/?post/2017/03/28/communication-instantanee-un-grand-pas-en-avant">toutes les applications de messagerie, c'est Signal qui a ma
préférence</a>. Une des raisonst est qu'elle n'appartient pas — comme les
deux autres — à un GAFAM.</p>
<p>Bref.</p>
<p>Il existe maintenant<sup id="fnref:f1"><a href="https://blog.namok.be/?post/2017/11/01/signal-desktop-la-version-desktop#fn:f1" rel="footnote">1</a></sup> une application <em>desktop</em> qui permet d'utiliser Signal
à partir de son ordinateur… et donc de bénéficier d'un vrai clavier.</p>
<p>Pour l'installer, j'adapte les infos <a href="https://signal.org/blog/standalone-signal-desktop/">en provenance du blog de Signal</a>
parce qu'avec <em>debian</em>, on n'est pas obligé d'utiliser <code>sudo</code>. Ce qui donne
donc:</p>
<p><code>curl -s https://updates.signal.org/desktop/apt/keys.asc | apt-key add -
echo "deb [arch=amd64] https://updates.signal.org/desktop/apt xenial main" | \
tee -a /etc/apt/sources.list.d/signal-xenial.list
apt update && apt install signal-desktop</code></p>
<p>… ce qui ne fonctionne pas pour moi car je dois, au préalable installer
<code>apt-transport-https</code> par</p>
<p><code>apt install apt-transport-https</code></p>
<p>Dès que c'est installé et lancé, il petit scan d'un QR-code à partir de mon
smartphone et tout roule.</p>
<p>Enjoy !</p>
<p><br/></p>
<p><em>Crédit photo chez <a href="https://cdn.gratisography.com/photos/439H.jpg">Gratisography</a>.</em></p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:f1">
<p>Avant, l'application qui existait était une <em>Chrome app</em> nécessitant
d'avoir installé Google chrome et de l'avoir lancé. Deux choses que je ne
fais pas car je préfère utiliser Firefox comme navigateur. Pas de Google
chrome chez moi (au moment où j'écris ce billet). <a href="https://blog.namok.be/?post/2017/11/01/signal-desktop-la-version-desktop#fnref:f1" rev="footnote">↩</a></p>
</li>
</ol>
</div>
Installation Lenovo ThinkPad X Carbon 14urn:md5:b6c8d9c583fbce7a9b2afb58cf1f27082017-06-12T23:06:00+02:002017-06-13T21:49:25+02:00PiTMes doigts dans le clavieralternativesbépodebiandiversgeeklogiciellibre<p><img src="https://blog.namok.be/public/images/divers/2017/machineecrire-madeira-1200x.jpg" alt="machineecrire-madeira-1200x.jpg" style="margin: 0 auto; display: block;" title="machineecrire-madeira-1200x.jpg, juin 2017" /></p>
<p>J'ai acheté un Lenovo en décembre dernier pour ma femmme et <a href="https://blog.namok.be/?post/2017/06/12/todo">l'installation
s'est passée sans aucun problème</a>. Une installation linux en
2017 n'a rien à voir avec une installation linux en 1995 !</p>
<p>Fini le CD d'install, une clé USB toute petite fait l'affaire.
Avec les connections réseaux actuelles, je recommande de ne télécharger
que <code>mini.iso</code> qui fait moins de 300Mb.</p>
<p><em>Toute ressemblance avec l'installation du Lenovo 13 n'est pas fortuite, je
me cite.</em></p>
<p>La première chose à faire est de se procurer une version de <em>debian</em> que l'on
pourra installer sur sa machine. Le plus simple est d'utiliser une (vielle)
clé usb… sans trop d'espace disponible<sup id="fnref:f1"><a href="https://blog.namok.be/?post/2017/06/12/install-lenovo-xcarbon#fn:f1" rel="footnote">1</a></sup>.</p>
<p>Pour télécharger l'iso, ça se passe <a href="https://www.debian.org/distrib/netinst">sur le site de Debian</a>. En bas
à droite, vous avez la section <em>network boot</em> et vous choisissez votre
architecture; <em>i386</em> ou <em>amd</em> ou… Vous pouvez télécharger le fichier <em>mini.iso</em>.</p>
<p>La copie de l'iso sur la clé est très simple<sup id="fnref:f31"><a href="https://blog.namok.be/?post/2017/06/12/install-lenovo-xcarbon#fn:f31" rel="footnote">2</a></sup>.</p>
<p><code>cp /elsewhere/mini.iso /dev/sdb
sync</code></p>
<p>Sur la machine à installer:</p>
<ul>
<li>allumer et entrer dans le BIOS / UEFI pour désactiver <em>secure boot</em> (
<em>disable secure boot</em> ou bien autoriser <em>legacy boot</em>).
Chez lenovo, il faut appuyer sur <strong>F1</strong>;</li>
<li>sauvegarder et continuer; </li>
<li>choisir de booter sur la clé USB en appuyant sur <strong>F12</strong> pour obtenir les
choix de boot;</li>
<li>… et se laisser guider par l'installeur debian.</li>
</ul>
<p>Personnellement, je le laisse se connecter automatiquement au réseau, je
formate le disque entier — je ne vois même pas le logo MS Windows, je
choisis XFCE comme environnement et « serveur ssh » et je le laisse
tranquille… en rédigeant cette note pour mes archives.</p>
<p>Au premier boot, je fais ces quelques tâches:</p>
<ul>
<li>remplacement de <em>network-manager</em> par <em>wicd</em>;</li>
</ul>
<p><code>apt remove network-manager
apt install wicd</code></p>
<ul>
<li>j'ajoute à <em>apt</em> les sources <em>contrib</em> et <em>non-free</em>;</li>
</ul>
<p><code>vim /etc/apt/sources.list
:%s/main /main contrib non-free</code></p>
<ul>
<li><p>paramétrage du <em>touchpad</em> pour qu'il accepte le « tapoter pour cliquer » via
l'interface graphique;</p></li>
<li><p>paramétrage de la gestion de l'énergie via l'interface graphique;</p></li>
<li><p>copie via <code>scp</code> — cette étape est facultative car <em>un peu plus technique</em>
— de mon répertoire contenant mes fichiers de conf de mon ancien portable
vers le nouveau. Ceci me permet par exemple de créer le répertoire <code>.vim</code>
avant d'installer <code>vim</code>… et mes confs sont là tout de suite !</p></li>
<li><p>installation de <em>owncloud-client</em>, encodage des identifiants… et les
fichiers arrivent;</p></li>
<li><p>j'en profite pour faire un <em>update</em> et passer à un noyau 4<sup id="fnref:f11"><a href="https://blog.namok.be/?post/2017/06/12/install-lenovo-xcarbon#fn:f11" rel="footnote">3</a></sup>;</p></li>
</ul>
<p>… il reste à régler les problèmes.</p>
<p>C'est ici que ça devient un peu plus <em>touchy</em>. De mémoire de linuxien, il
y a toujours bien un p'tit truc qui ne fonctionne pas bien tout de suite… et
ce n'est pas bien grave. C'est le moment d'en profiter pour faire un <a href="https://www.debian.org/donations">don
à debian</a> afin que les petits problèmes se règlent plus vite.</p>
<p>D'expérience, je sais que la <strong>carte son</strong> ou la <strong>caméra</strong> risque de ne pas
fonctionner. Bien que je n'utilise quasi jamais la caméra — chatroulette,
c'est <em>has been</em> — je me connecte à <code>http://meet.jit.si/esi</code> et je me vois et
je m'entends. Tout va bien, je peux placer le post-it sur la caméra et
l'oublier.</p>
<p>J'ai un <strong>lecteur d'empreinte</strong> que j'ai désactivé dans l'UEFI.</p>
<p>Les <strong>touches volume</strong> ne fonctionnent pas… et je ne vois pas le plugin audio dans
mon tableau de bord…</p>
<p><code>apt install xfce4-pulseaudio-plugin</code></p>
<p>… ça marche.</p>
<p>Les <strong>touches luminosité</strong> ne fonctionnent pas. Une <a href="https://itsfoss.com/fix-brightness-ubuntu-1310/">recherche plus
tard</a> et j'ajoute ce fichier:</p>
<p><code>cat /usr/share/X11/xorg.conf.d/20-intel.conf
Section "Device"
Driver "intel"
Option "Backlight" "intel_backlight"
Identifier "card0"
EndSection</code></p>
<p>La <strong>carte wifi</strong>. <code>lspci</code> me montre que ma carte wifi est une <em>intel 8260</em>
(<em>Detected Intel(R) Dual Band Wireless AC 8260</em> (qui est prise en charge par
le module <em>iwlwifi</em>. Ça ne marche pas. Un <code>apt search iwlwifi</code> montre que
<code>firmware-iwlwifi</code> n'est pas installé. Je l'installe. Ça ne fonctionne pas.</p>
<p>Je fais un petit tour par les forums et les <em>logs</em>. Ils me montrent qu'il
y a un soucis avec la version 22 du firmware. J'efface cette version et c'est
la version 21 qui sera chargée et qui est fonctionnelle<sup id="fnref:f21"><a href="https://blog.namok.be/?post/2017/06/12/install-lenovo-xcarbon#fn:f21" rel="footnote">4</a></sup>.</p>
<p><code>rm /lib/firmware/iwlwifi-8000C-22.ucode</code></p>
<p>Après suppression de ce fichier, la carte wifi fonctionne.</p>
<p>Mon clavier a une touche <strong>[PrintScr]</strong> à droite de la barre d'espace et pas
de touche « <em>Menu contextuel</em> » et ça m'ennuie. Pour changer ça, je récupère
le <em>keycode</em> de la touche grâce à <code>xev</code>. C'est 107. Je crée ensuite un fichier
<code>~/.xmodmaprc</code> redéfinissant l'action à exécuter lors de l'appui sur la touche
comme suit:</p>
<p><code>keycode 107 = Menu NoSymbol Menu</code></p>
<p>Et pour que ce fichier soit exécuté, j'ajoute au fichier <code>~/.xsessionrc</code> — que
je crée s'il n'existe pas:</p>
<p><code>xmodmap ~/.xmodmaprc</code></p>
<p>J'utilise un tout petit peu <strong>dropbox</strong> (c'est presque historique).
Après l'installation via le site web, j'ai une erreur de
<em>"Can't monitor folder"</em>. Je lis les logs — <code>tail -f /var/log/syslog</code> — et j'y
trouve la solution:</p>
<ul>
<li>éditer <code>/etc/systctl.conf</code>;</li>
<li>ajouter la ligne <code>fs.inotify.max_user_watches = 100000</code>;</li>
<li>relancer <code>sysctl -p</code>;</li>
<li>… et ça synchronise</li>
</ul>
<p>And voilà, il reste à coller les autocollants <strong>bépo</strong> ;-)<br />
Enjoy.</p>
<p><br/></p>
<p><em>Photo d'une toute vielle machine à écrire prise à Madère. Le nouveau laptop est
un peu plus récent.</em></p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:f1">
<p>Comme l'on va copier un <em>iso</em> dessus, la place disponible restante sera
inutilisable. Cette clé USB ne servira donc qu'à avoir une <em>debian</em> à installer… comme
un bon vieux CD d'installation. <a href="https://blog.namok.be/?post/2017/06/12/install-lenovo-xcarbon#fnref:f1" rev="footnote">↩</a></p>
</li>
<li id="fn:f31">
<p>Prenez la peine de vérifier que <em>sdb</em> est bien la clé USB et pas votre
disque dur… <a href="https://blog.namok.be/?post/2017/06/12/install-lenovo-xcarbon#fnref:f31" rev="footnote">↩</a></p>
</li>
<li id="fn:f11">
<p>Je pense que c'est nécessaire pour qu'il n'y ait pas de soucis de mise
en veille. <a href="https://blog.namok.be/?post/2017/06/12/install-lenovo-xcarbon#fnref:f11" rev="footnote">↩</a></p>
</li>
<li id="fn:f21">
<p>Par contre, ma carte <em>wlan0</em> est renommée en <em>wlp4s0</em>. <a href="https://blog.namok.be/?post/2017/06/12/install-lenovo-xcarbon#fnref:f21" rev="footnote">↩</a></p>
</li>
</ol>
</div>
Les logiciels libres ou open source ne sont pas (nécessairement) gratuitsurn:md5:c3e7f3791e68e65987d453d7566948d82017-05-18T12:58:00+02:002017-05-18T12:02:09+02:00PiTMes doigts dans le clavieralternativesdebiandiversdotcleargeeklibreofficelogiciellibremastodonopenofficepandoc<p><img src="https://blog.namok.be/public/images/divers/2017/lance-anderson-245146-1400x.jpg" alt="lance-anderson-245146-1400x.jpg" style="margin: 0 auto; display: block;" title="lance-anderson-245146-1400x.jpg, mai 2017" /></p>
<p>Un logiciel libre est un logiciel répondant à ces libertés essentielles;
l'exécution, l'étude, la copie, la distribution, la modification et
l'amélioration.</p>
<p><em>Think free as "free speech" not "free beer"</em>.</p>
<blockquote>
<p>L'utilisateur d'un programme libre jouit des quatre libertés essentielles :</p>
<ul>
<li>la liberté d'exécuter le programme comme il le souhaite, pour n'importe
quel usage (liberté 0);</li>
<li><p>la liberté d'étudier le fonctionnement du programme et de l'adapter à ses
besoins (liberté 1) – une condition préalable est d'accéder au code source;</p></li>
<li><p>la liberté d'en redistribuer des copies pour aider les autres (liberté
2);</p></li>
<li>la liberté d'améliorer le programme et de rendre publiques ses
améliorations pour que toute la communauté en bénéficie (liberté 3) – une
condition préalable est d'accéder au code source.</li>
</ul>
<p>Extrait de <a href="https://www.gnu.org/home.fr.html">GNU free software fundation</a></p>
</blockquote>
<p>Les logiciels libres ou <em>open source</em><sup id="fnref:f1"><a href="https://blog.namok.be/?post/2017/05/18/logiciels-libres-ou-open-source-ne-sont-pas-gratuits#fn:f1" rel="footnote">1</a></sup> sont librement distribués et
distribuer un logiciel, c'est le donner ou le vendre. Chacun est libre de
vendre un logiciel libre et chacun est libre de l'acheter ou non. La <em>free
software fundation</em> <a href="https://www.gnu.org/philosophy/selling.fr.html">encourage même ceux qui distribuent un logiciel libre
à le faire payer</a>.</p>
<p>Systématiquement associer « gratuit » à « libre » (ou <em>open source</em>) peut
vider le logiciel libre de sa valeur. Les logiciels libres ont de la valeur.
Le cout du développement n'est évidemment pas négligeable et les développeurs
sont en partie des développeurs salariés d'une entreprise qui trouve
son intérêt dans le logiciel libre et, pour l'autre partie, des passionnés qui
contribuent aux communs.</p>
<p>Si un logiciel libre est gratuit — et ce n'est pas une obligation ni un
argument — son utilisation ne l'est pas forcément.
Utiliser des logiciels libres n'est pas gratuit<sup id="fnref:f2"><a href="https://blog.namok.be/?post/2017/05/18/logiciels-libres-ou-open-source-ne-sont-pas-gratuits#fn:f2" rel="footnote">2</a></sup>, ça à un cout. Le cout
d'installation, de configuration, de maintenance, de mise à jour… Ces couts
peuvent être les mêmes que pour les logiciels propriétaires.</p>
<p>Les logiciels libres ne sont pas gratuits, ils sont libres d'usage.</p>
<p><br/></p>
<p><em>Crédit photo par <a href="https://unsplash.com/photos/82fcB8ZgdNI">Lance Anderson</a>. Quelque chose comme free beer.</em></p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:f1">
<p>Je ne m'étends pas sur la différence entre « logiciel libre » et
« logiciel open source » car les deux sont très proches… et souvent
confondus. Outre les différences techniques, je cite <a href="http://gabriellacoleman.org/">Gabriella
Coleman</a> sur la différence philosophique entre les deux:
«<em>Le terme "logiciel libre" met l'accent sur le droit à apprendre et à
accéder aux connaissances, tandis que le terme "open source" tend à mettre en
avant les avantages pratiques.</em> » <a href="https://blog.namok.be/?post/2017/05/18/logiciels-libres-ou-open-source-ne-sont-pas-gratuits#fnref:f1" rev="footnote">↩</a></p>
</li>
<li id="fn:f2">
<p>Je ne parle pas ici de l'utilisation de LibreOffice par rapport à la
suite bureautique de Microsoft par exemple. Cette utilisation est gratuite…
hormis l'énergie que l'on peut dépenser si l'on veut en faire le
prosélytisme. <a href="https://blog.namok.be/?post/2017/05/18/logiciels-libres-ou-open-source-ne-sont-pas-gratuits#fnref:f2" rev="footnote">↩</a></p>
</li>
</ol>
</div>