mardi 8 décembre 2015

Mise à niveau vers Fedora 23

Je suis passé de Fedora 22 à Fedora 23 à l'aide des commandes suivantes:

[root@rigel ~]# dnf update --refresh

pour mettre à jour le système existant

[root@rigel ~]# dnf install dnf-plugin-system-upgrade

pour installer le programme qui convient

 [root@rigel ~]# dnf system-upgrade download --releasever=23

pour télécharger les paquets de la nouvelle version

[root@rigel ~]# dnf system-upgrade reboot

pour procéder à l'installation proprement dite.

En principe il n'y a pas de soucis.

Cependant si vous avez procédé à l'installation de chromium et de pepper-flash suivant ce qui est préconisé ici, il ne faut pas oublier de réactiver les dépôts russianfedora-free:

[root@rigel ~]# sed -i 's/enabled=0/enabled=1/' /etc/yum.repos.d/russianfedora-free.repo

[root@rigel ~]# sed -i 's/enabled=0/enabled=1/' /etc/yum.repos.d/russianfedora-free-updates.repo

En ce qui concerne google-earth, si un dépôt est configuré et activé, il faut le supprimer:

[root@rigel ~]# rm /etc/yum.repos.d/google-earth.repo

sous peine d'avoir une erreur qui empêche la mise à jour (c'est la même que celle dont il est question ci-après).

Le paquet téléchargé ici pour Fedora 64 bits (à savoir le paquet google-earth-stable_current_x86_64.rpm) n'est pas installable car:

Erreur : Erreur du contrôle de transaction
  le fichier /usr/bin de l'installation de google-earth-stable-7.1.4.1529-0.x86_64 entre en conflit avec le fichier du paquet filesystem-3.2-35.fc23.x86_64

Cette commande:

toto@rigel:~/Téléchargements$ rpmrebuild -p -s fichier google-earth-stable_current_x86_64.rpm && grep '/usr/bin' fichier
%dir %attr(0755, root, root) "/usr/bin"
%attr(0777, root, root) "/usr/bin/google-earth"

nous montre où est le problème: il faut supprimer la première ligne de l'output.

Je lance la commande:

toto@rigel:~/Téléchargements$ rpmrebuild -p -e google-earth-stable_current_x86_64.rpm 

qui donne la possibilité de modifier le fichier spec.

En principe l'éditeur est est vi. On y tape successivement les commandes

/usr\/bin
dd
:wq

A la question posée (veux-t-on continuer) la réponse à fournir est évidemment 'Oui', ce qui provoque la création d'un nouveau rpm qu'il reste à installer:

[root@rigel ~]# dnf install /home/toto/rpmbuild/RPMS/x86_64/google-earth-stable-7.1.4.1529-0.x86_64.rpm 

Pour que la recherche d'un lieu dans google-earth fonctionne correctement j'ai créé un alias:

alias google-earth='LC_NUMERIC=C google-earth'

(commande que j'ai mise dans mon fichier .bashrc)

Le lancement de google-earth en ligne de commande produit une série de messages d'erreurs:

1208/114257:ERROR:nss_ocsp.cc(581)] No URLRequestContext for OCSP handler.

Cependant google-earth ne crashe pas.

Si c'est le cas, il suffit de supprimer le dossier .googleearth:

[toto@rigel ~]$ rm -rf .googleearth/



mercredi 21 octobre 2015

sed utile

Et oui, c'est utile (sed utile)! La preuve par la suite.
Partant d'un fichier pdf, nous construisons chaque mois un fichier texte reprenant les dépenses liées à l’utilisation  d'une carte bancaire et ce en vue d'une importation dans une base de données.
Tout était OK jusqu'à ce que brusquement le fichier texte obtenu ne convienne plus alors que apparemment au niveau du fichier pdf rien n'était changé.
Ce fichier texte inadéquat se présentait comme suit:

blabla1 15,65
EUR
blabla2 58,49
EUR
blabla3 214,29
EUR
blabla4 1.028,56
EUR

La première anomalie (le nom de la devise sur une ligne à part) peut se résoudre facilement avec la commande sed:



Explications:
sed importe dans son espace de travail la première ligne du fichier cbxy avant d'exécuter le script, à savoir le texte entre apostrophes. Ce script comprend 2 commandes:

  1. N provoque l'importation de la ligne 2 dans l'espace de travail
  2. s/\n/ / remplace par un espace le caractère de fin de ligne (\n) qui sépare les deux lignes: elles sont concaténées.
La fin du script déclenche l'impression automatique de l'espace de travail puis sed passe à la lecture de la ligne suivante, c'est-à-dire la ligne 3 (blabla2).

Le problème serait donc résolu s'il n'y avait qu'une seule anomalie. Hélas une autre anomalie est apparue: la disparition du signe moins (-) devant les montants (alors que ce signe est bel et bien présent dans le fichier pdf).
De nouveau sed va nous aider:



L'option r enjoint à sed d'utiliser les expressions régulières étendues. L'option n désactive l'impression automatique de l'espace de travail, par contre le p qui se trouve à la fin du script provoque son impression à chaque substitution. De la sorte nous recevons en output seulement les lignes qui nous intéressent.
Dans la commande de substitution

s/ ([0-9]{1,3},[0-9]{2})$/ -\1/

le motif recherché se trouve en fin de ligne (symbole $). Il est constitué d'un espace suivi de 1 à 3 chiffres, une virgule puis encore 2 chiffres. Dans la chaîne de remplacement \1 désigne la partie de la chaîne trouvée correspondant à l'expression régulière qui est entre parenthèses. L'utilisation des parenthèses permet ainsi d'éviter que le signe moins ajouté soit suivi d'un espace.

La ligne blabla4 manque à l'appel. Elle peut être traitée comme ceci:


Cette fois la commande de substitution est:

s/[0-9]{1,3}\.[0-9]{3},[0-9]{2}$/-&/

La chaîne recherchée située en fin de ligne doit correspondre au motif suivant:
1 à 3 chiffres suivi d'un point, puis 3 chiffres une virgule et encore 2 chiffres (dans l’expression régulière le point est échappé ou sinon il correspondrait à n'importe quel caractère). Dans la chaîne de remplacement, & désigne la totalité de la chaîne trouvée.

Pour transformer cbxy tout en conservant les commandes à exécuter, nous créons le fichier cbxy.sed:


#!/bin/sed -rf
s/[0-9]{1,3}\.[0-9]{3},[0-9]{2}$/-&/
s/ ([0-9]{1,3},[0-9]{2})$/ -\1/
N
s/\n/ /

Nous le rendons exécutable avant utilisation:


Tout semble OK. On termine avec la commande:

[toto@rigel cbxy]$ ./cbxy.sed cbxy > cbxyok

samedi 3 octobre 2015

Listbox

Nous sommes dans libreoffice base, connecté à une notre base de données postgresql bdtest.
Ci-après une représentation schématique des tables qui vont être concernées dans la suite de cet article:

Les relations d'intégrité référentielle sont représentées par des flèches pointant vers les clefs primaires cibles.
La colonne "service" de la table "gestion" contient le code du service auquel appartient l'employé, tandis que le nom de ce service se trouve dans la table "services".
Nous avons déjà réalisé de nombreux formulaires relatifs à la table "employés" (voir par exemple ici), mais cette fois nous allons construire un formulaire très simple sur la base duquel nous allons présenter en détail la notion de zone de liste (listbox). L'utilité d'une telle zone est de pouvoir montrer à l'utilisateur une liste déroulante d’éléments parmi lesquels celui-ci peut faire un choix. A chaque élément de la liste lui correspond un autre qui est caché. Si l'élément affiché est changé, la table du formulaire est mise à jour non pas avec l'élément choisi mais avec l'élément caché correspondant. Différentes possibilités sont offertes pour le type de contenu de liste. Nous allons considérer les quatre types de contenu de liste les plus courants, à savoir: instruction sql, requête, table, liste de valeurs.

Formulaire de base
Avant de nous attaquer à cette notion de zone de liste, utilisons l'assistant pour construire le formulaire. Dans la première étape choisissons la table "employés" comme source de données et nous en sélectionnons tous les champs. Ensuite l'assistant nous propose la création d'un sous-formulaire contenant les données de la table "gestion":


(Dans bdtest existe aussi une relation de la table "paie" vers "employés" mais elle ne nous intéresse pas ici)

Cette fois n'afficherons dans le formulaire que le seul champ "service":


Pour la position des contrôles nous choisissons deux fois l'option "colonnes, étiquettes en haut".
En ce qui concerne la saisies des données, nous cochons ce qui nous convient:


Lorsque nous en avons fini avec l'assistant, il nous reste à repositionner et redimensionner les différents champs et choisir éventuellement une image de fond pour le formulaire.
Nous n'oublions pas de mettre en "lecture seule" le champ matricule. Pour ce faire nous le sélectionnons (CTRL +clic gauche) avant d'utiliser l'outil contrôle:




Utilisons l'outil "navigateur de formulaire"


afin de renommer MainForm et SubForm en Employés et Gestion:


Pour ce faire, il suffit de cliquer droit sur MainForm et SubForm et de choisir "Renommer" dans le menu contextuel qui surgit.
Cette étape n'est pas nécessaire mais apporte un plus au niveau clarté.
Il nous reste à affiner quelque peu les propriétés de "Employés" et de "Gestion". On y accède à travers le navigateur de formulaire via le menu contextuel de ces 2 éléments.
Pour "Employés" on demande un tri sur le matricule:


Pour "Gestion" nous souhaitons une barre de navigation qui soit celle de "Employés":


Remarquons en passant que l'assistant à correctement lié le formulaire "Gestion" à son fortmulaire parent.

Comme déjà indiqué le champ "service" contient seulement un code. Pour que le formulaire affiche en clair le nom du service, 2 possibilités s'offrent à nous:

  • Un sous-formulaire avec les données de la table "services", sous-formulaire lié à "Gestion"
  • Une zone de liste (listbox)
Cette article cible la deuxième possibilité. Cependant nous demandons au lecteur de faire preuve d'un peu de patience car pour mieux mettre en évidence les avantages de cette solution 2, nous allons d'abord procéder avec la solution 1 (mais le lecteur peut évidemment passer directement à la section "zone de liste").

Sous-formulaire "Services"
Cliquant droit sur "Gestion" nous créons un nouveau formulaire:


formulaire que nous renommons aussitôt "Services" avant de fixer ses propriétés sur le panneau adéquat obtenu via le menu contextuel:


Le lien avec la table parent (gestion) a du être ajouté manuellement et toucher à la table "services" ne sera pas autorisé.
Cela étant, le formulaire "Services" restant sélectionné, nous utilisons l'outil "ajouter un champ"


pour ajouter le champ "dénomination":


Après repositionnement, redimensionnement et suppression de l'étiquette du champ, on arrive à quelque chose comme ceci:



Quand un code de service est encodé, un appui sur ENTER projette le focus dans le champ suivant (celui qui contient le nom du service). Le focus passant du formulaire "Gestion" au formulaire "Services", cette action provoque l'enregistrement du nouveau code dans la base de données. Cependant le formulaire doit encore être rafraîchit pour que le nom du service soit actualisé.
Pour automatiser cela, il suffit d'assigner la macro

'*************************************
Sub ReloadTableOne
'*************************************
    Dim Frm as object
    Frm=Thiscomponent.DrawPage.Forms.getByIndex(0)
    Frm.reload()
End Sub

à l’événement "Après l'action d’enregistrement"  figurant dans l'onglet "Événements" des propriétés du formulaire "Gestion" (voir ce billet).

En l'absence d'intégrité référentielle n'importe quel code peut-être introduit même si aucun service n'y correspond.
Heureusement ce n'est pas le cas ici car postgresql gère l'intégrité référentielle: 


Zone de liste "service"
Procédons maintenant avec la deuxième possibilité: la création d'une zone de liste ce qui nous permettra d'afficher les données de la table "services" sans utilisation du sous-formulaire "Services".
Le champ "service" étant sélectionné, nous cliquons droit afin d'accéder au menu contextuel pour pouvoir remplacer le champ par une zone de liste:


Nous redimensionnons la zone puis nous accédons aux propriétés du contrôle:


Nous constatons que le champ de données sélectionné est bien  ce qui est attendu, à savoir "service".
Si le contenu de la liste est basé sur une table, seront affichés les éléments de la première colonne de cette table. Le numéro "Champ lié" indique lui la colonne (comptée à partir de 0) dont les éléments serviront pour la mise à jour éventuelle du champ "service". Ce sont les fameux éléments cachés (ils ne seront pas affichés). Mais dans la table "services", c'est tout le contraire! La colonne "dénomination"  qui contient les éléments à afficher est la deuxième, tandis que la colonne des éléments cachés est la première. Nous contournons le problème en prenant comme contenu de la liste l'instruction SQL:

SELECT dénomination, service_id
FROM services
ORDER by service_id ASC

Une autre solution serait de créer une requête libreoffice avec l'instruction ci-après et de choisir cette requête comme contenu de la liste.

Dans l'onglet général nous indiquons que la liste doit être déroulante:


Et voilà: pour changer un employé de service, il suffit que l'encodeur choisisse un nouveau nom de service dans la liste déroulante. Ce nouveau service apparaît en clair à l'écran avant même tout enregistrement. Le code du service de l'employé, figurant dans la table gestion, sera mis à jour avec service_id et ce de manière tout à fait transparente pour l'utilisateur.
De plus, indépendamment de toute intégrité référentielle, celui-ci ne peut entrer que des services existants.

Pour illustrer l’utilisation d'une table comme source de données pour une liste, créons la table servicesn (n pour nombre) où la colonne d'indice o (colonne 1) est bien la colonne "dénomination":

CREATE TABLE servicesn (
    "dénomination" character(25),
nombre integer,
    service_id character(2) NOT NULL
)

La colonne nombre est destinée à contenir le nombre d'employés pour chaque service. Nous la remplissons avec:

update servicesn
set nombre =
(select count(*) from gestion
where service = service_id)

Dans cas, l'onglet "Données" du panneau des propriétés de la liste se présenterait comme suit:


La colonne "Champ lié" (colonne des éléments cachés) est la colonne d'indice 2 (colonne 3).
Il n'est pas possible ici d'introduire via le panneau des propriétés une notion de tri. Il faudrait donc que l'ordre naturel de la table soit celui souhaité.

Nous avons déjà évoqué 3 possibilités pour le type du contenu de liste:
sql, requête, table.
Une dernière est possibilité est "Liste de valeurs".
Dans ce cas il faut encoder les valeurs qui peuvent servir pour la mise à jour du champ "service" donc dans notre cas le code des services:


La notion de champ lié est alors inopérante et il faut encoder sous l'onglet Général les valeurs qui seront présentées à l'utilisateur, soit le nom des services:


La table "services" est inutilisée, mais ceci ne semble pas être une solution satisfaisante car les valeurs en question doivent se trouver sur tous les postes clients.

mercredi 29 juillet 2015

freshplayer

Pour linux, le plugin flashplayer fournit par Adobe est resté bloqué sur la version 11. Il devient peu à peu obsolète. Par contre le navigateur chrome de google vient avec un plugin intégré, pepperflash qui lui continue à évoluer. Ce plugin peut-être utilisé par chromium mais pas directement par firefox.
Bienvenu soit le plugin freshplayer qui permet l'utilisation de pepperflash avec firefox. Pour que ça fonctionne, il faut disposer de pepperflash. Le plus simple pour ce faire est d'installer d'abord chromium puis le plugin pepperflash (car celui-ci n'est le plus souvent pas intégré dans chromium).
Pour Ubuntu ces deux installations sont assez évidentes. Rappelons cependant que la mise à jour de pepperflash n'est pas automatique et peut se faire avec la commande:

root@rigel:~# /update-pepperflashplugin-nonfree --install

Pour Fedora nous avons indiqué ici comment procéder à l'installation de chromium (et de pepperflash). Il faut évidemment adapter les commandes à la version de Fedora. Pour Fedora 22, remplacer 20 par 22 et éventuellement yum par dnf.
On peut aussi carrément installer chrome comme expliqué par exemple ici pour Fedora. Le site est en anglais, aussi nous reprenons brièvement ce qu'il convient de faire:
D'abord devenir root:

toto@rigel:~$ su -
Mot de passe : 
[root@rigel ~]

Ensuite pour installer le dépôt ad hoc, coller le texte:

cat << EOF > /etc/yum.repos.d/google-chrome.repo
[google-chrome]
name=google-chrome - \$basearch
baseurl=http://dl.google.com/linux/chrome/rpm/stable/\$basearch
enabled=1
gpgcheck=1
gpgkey=https://dl-ssl.google.com/linux/linux_signing_key.pub
EOF

et poursuivre par un appui sur ENTER puis procèder à l'installation avec:


[root@rigel ~]# dnf install google-chrome-stable

En ce qui concerne l'installation du plugin freshplayer, pour Ubuntu il existe un PPA qu'il suffit d'ajouter

toto@rigel:~$ sudo add-apt-repository ppa:nilarimogard/webupd8

Ensuite :

toto@rigel:~$ sudo apt-get update

Puis:

toto@rigel:~$ sudo install freshplayerplugin

Pour Fedora, il s'agit de compiler freshplayerplugin à partir de la source.
Installons d'abord ce qui convient (les dépôts rpmfusion ont été configurés):

[root@rigel ~]# dnf install glib2-devel libX11-devel uriparser-devel cairo-devel pango-devel freetype-devel gtk2-devel gcc-c++ alsa-lib-devel libconfig-devel libevent-devel mesa-libGLES-devel git cmake ragel

Ensuite procédons avec les commandes;

[root@rigel ~]# git clone https://github.com/i-rinat/freshplayerplugin.git
...
[root@rigel ~]# cd freshplayerplugin 
[root@rigel fresplayerplugin]# mkdir build 
[root@rigel freshplayerplugin]# cd build 
[root@rigel build]# cmake ..

(Les .. qui suivent la commande cmake sont essentiels; ils désignent le répertoire supérieur)

En image:


Le paquet openssl est manquant. Pourtant il est installé. Mais lors d'une compilation ce qui est nécessaire ce sont les paquets de développement.
Donc nous installons openssl-devel:

[root@rigel build]# dnf install openssl-devel
...
[root@rigel build]# cmake ..

Ensuite pour répondre aux autres messages issus par cmake et qui indiquent des paquets manquants:

[root@rigel build]# dnf install ffmpeg-devel libva-devel libvdpau-devel libv4l-devel
...
[root@rigel build]# cmake ..

Pour finir, si tout est OK:


[root@rigel build]# make
...
[root@rigel build]# cp libfreshwrapper-pepperflash.so /usr/lib64/mozilla/plugins/

Cela fonctionne pour autant que l'on ait désinstallé (ou désactivé le flashplayer de Adobe)

Pour d'autres distributions, freshplayerplugin se trouve directement dans les dépôts.

samedi 13 juin 2015

redirections revisitées

Pourquoi revisitées?
Parce que nous avons déjà traité du sujet dans ce billet et dans celui-ci.
Et aussi parce que l'approche sera cette fois quelque peu différente.
Dans un répertoire test, nous lançons les commandes suivantes touch et ls:

toto@aldebaran:~/test$ touch titi
toto@aldebaran:~/test$ ls titi tata
ls: impossible d'accéder à tata: Aucun fichier ou dossier de ce type
titi

La commande ls nous fournit 2 lignes en sortie:

  • un message qui nous informe que le fichier tata n'existe pas
  • le nom du fichier titi qui vient d'être créé par touch

Le message est venu via le canal de sortie des erreurs (stderr), titi (nom du fichier) via la sortie standard (stdout).
Stdout est géré par le système via le descripteur de fichier #1, tandis que stderr est lié au descripteur de fichier #2
Le descripteur de fichier #0 correspond quant à lui à l'entrée standard (stdin).
Ces fichiers se trouvent dans le répertoire /proc/pid/fd où pid est l'identifiant du processus concerné:


(Ici l'identifiant du processus ls est 2609).

Ce répertoire n'existe que pendant la durée de vie du processus.
Les fichiers /proc/self/fd sont hérités de ceux existant dans le shell d'où est lancé le processus

Examinons le résultat suivant:

toto@aldebaran:~/test$ ls titi tata 2>&1 1>/dev/null
ls: impossible d'accéder à tata: Aucun fichier ou dossier de ce type
toto@aldebaran:~/test$ 

2>&1 : le descripteur 2 prend toutes les caractéristiques du descripteur 1. Les messages d'erreur sont dirigés suivant le canal correspondant au descripteur 1 (canal 1).
1>/dev/null : le descripteur 1 pointe vers /dev/null. La sortie standard est envoyée vers le néant. Cela ne modifie en rien les nouvelles caractéristiques acquises juste avant par le descripteur 2.

Remarquons que la première redirection ne change rien: les messages d'erreur seraient de toute façon arrivés à l'écran.
Toutes ces redirections sont éphémères: elles s'effectuent au niveau des descripteurs qui se trouvent dans /proc/self/fd.
Il est possible de définir des redirections au niveau du shell comme ici:

toto@aldebaran:~/test$ echo $$
2959
toto@aldebaran:~/test$ exec 3>&1 1>sortie
toto@aldebaran:~/test$ ls titi
toto@aldebaran:~/test$ echo
toto@aldebaran:~/test$ ls -l /proc/2959/fd/[0-3]

Avec la commande echo $$ nous cherchons à connaître l'identifiant du processus (pid) correspondant au shell dans lequel nous sommes.
La commande suivante crée un descripteur 3 qui reprend toutes les caractéristiques du descripteur 1 (ce descripteur 3 constitue en quelques sorte une sauvegarde du descripteur 1), puis nous faisons pointer ce descripteur 1 vers le fichier sortie.
Dorénavant, seuls les messages d'erreur arriveront à l'écran: les deux commandes ls suivantes n'affichent aucun résultat puisque les descripteurs au niveau des processus fils (processus des commandes) sont hérités de ceux qui existent au niveau du shell. Dans le cas de deux commandes reliées par un tube (pipe), cette dernière affirmation n'est pas tout a fait exacte.
Vérifions:

toto@aldebaran:~/test$ echo
toto@aldebaran:~/test$ ls -l /proc/self/fd/[0-3] | cat
toto@aldebaran:~/test$ echo
toto@aldebaran:~/test$ ls titi | ls -l /proc/self/fd/[0-3]

La première commande ls fournira les descripteurs du processus en amont du tube.
La commande en aval, cat, recopie simplement l'entée standard vers la sortie standard.
La troisième commande ls nous informera sur les descripteurs au niveau du processus en aval (la commande ls titi n'enverra rien dans le fichier sortie, ni à l'écran, mais il faut une commande en amont).
Les commandes echo ont simplement pour but de rendre le fichier sortie plus lisible.
Examinons maintenant le contenu du fichier sortie à l'aide de la commande cat, en n'oubliant pas que le descripteur 3 est une sauvegarde du descripteur 1:

toto@aldebaran:~/test$ cat sortie >&3
titi

lrwx------ 1 toto toto 64 jun 12 15:19 /proc/2959/fd/0 -> /dev/pts/1

l-wx------ 1 toto toto 64 jun 12 15:19 /proc/2959/fd/1 -> /home/toto/test/sortie
lrwx------ 1 toto toto 64 jun 12 15:19 /proc/2959/fd/2 -> /dev/pts/1
lrwx------ 1 toto toto 64 jun 12 15:19 /proc/2959/fd/3 -> /dev/pts/1

lrwx------ 1 toto toto 64 jun 12 15:20 /proc/self/fd/0 -> /dev/pts/1

l-wx------ 1 toto toto 64 jun 12 15:20 /proc/self/fd/1 -> pipe:[36369]
lrwx------ 1 toto toto 64 jun 12 15:20 /proc/self/fd/2 -> /dev/pts/1
lrwx------ 1 toto toto 64 jun 12 15:20 /proc/self/fd/3 -> /dev/pts/1

lr-x------ 1 toto toto 64 jun 12 15:21 /proc/self/fd/0 -> pipe:[36372]

l-wx------ 1 toto toto 64 jun 12 15:21 /proc/self/fd/1 -> /home/toto/test/sortie
lrwx------ 1 toto toto 64 jun 12 15:21 /proc/self/fd/2 -> /dev/pts/1
lrwx------ 1 toto toto 64 jun 12 15:21 /proc/self/fd/3 -> /dev/pts/1
toto@aldebaran:~/test$ 

Dans le processus en amont, fd/1 pointe vers le tube et n'a donc aucun rapport avec fd/1 au niveau du shell (qui pointe vers sortie).
Dans le processus en aval, l'entrée standard est alimentée par la sortie du tube: donc la sortie standard du processus en amont est connectée via le tube (pipe) à l'entrée standard du processus en aval.
Les deux tubes (pipe) portent des numéros différents car ils concernent deux lignes de commandes différentes.
Essayons d'écrire une ligne de commande qui affichent les descripteurs en amont et en aval d'un tube.
Tout d'abord nous réinitialisons le descripteur 1 à partir de sa sauvegarde puis nous clôturons le descripteur 3, tout cela avant de procéder:


Ça ne fonctionne pas: la commande en amont ne fournit aucun résultat car celui-ci est réceptionné à droite du tube via l'entrée standard qui n'est pas utilisée par la commande ls en aval.
Nous allons rediriger le résultat de la commande en amont vers stderr de cette commande pour éviter le tube. Mais par là même du fait de vouloir visualiser un résultat, nous allons le modifier. Cela rappelle un peu la théorie de la mécanique quantique qui dit que l'observation d'un phénomène modifie ce phénomène. Aussi nous allons avant tout sauvegarder l'état initial du descripteur 1 amont dans un descripteur 3. Cette fois tout est OK:


Revenons sur la commande ls titi tata 2>&1 1>/dev/null que nous avons considérée au début. Si cette commande se trouve en amont d'un tube, la redirection 2>&1 (qui ne servait à rien) trouve cette fois toute son utilité: les messages d'erreur sont envoyés à l'écran via le tube ce qui n'est pas le cas autrement:


toto@aldebaran:~/test$ ls titi tata 2>&1 1>/dev/null | tr t b
ls: impossible d'accéder à baba: Aucun fichier ou dossier de ce bype
toto@aldebaran:~/test$ ls titi tata 1>/dev/null | tr t b
ls: impossible d'accéder à tata: Aucun fichier ou dossier de ce type
toto@aldebaran:~/test$ 

On voudrait maintenant que la sortie standard arrive également sur l'écran mais sans transiter par le tube (pipe).
Ceci réalise notre souhait:

toto@aldebaran:~/test$ ls titi tata 3>&1 1>&2 2>&3 | tr t b
titi
ls: impossible d'accéder à baba: Aucun fichier ou dossier de ce bype
toto@aldebaran:~/test$
(c'est bien titi qui est affiché et non bibi)

Nous créons tout d'abord le descripteur 3 en tant que copie du descripteur 1. Ensuite le descripteur 1 acquiert toutes les caractéristiques du descripteur 2, la sortie standard est donc envoyée sur le canal 2. Le canal 1 est perdu, heureusement nous en avons une copie  (le canal 3 qui conduit au tube) vers lequel nous envoyons la sortie des erreurs.

Si par contre le descripteur 3 est créé au niveau du shell, il lui correspond un canal qui arrive sur l'écran (puisque fd/3 est alors une copie de fd/1 au niveau du shell).
Au niveau de la commande, nous pouvons utiliser les mêmes redirections qu'avant, mais il est plus simple de procéder comme ceci:

toto@aldebaran:~/test$ ls titi tata 2>&1 1>&3 | tr t b
ls: impossible d'accéder à baba: Aucun fichier ou dossier de ce bype
titi
toto@aldebaran:~/test$ 

D'autres détails concernant notamment stdin, peuvent être trouvés dans ce billet.

mardi 2 juin 2015

souris qui s'endort

Quoi de plus énervant que d'avoir une souris qui s'endort tout le temps: après quelques secondes d'inactivité, il faut chaque fois la réveiller avec un clic.
Essayons de trouver la source du problème.
Utilisons tout d'abord lsusb:

[toto@rigel ~]$ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 002: ID 0461:4d64 Primax Electronics, Ltd 
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
[toto@rigel ~]$ 

La deuxième ligne correspond à notre souris.

Cherchons des infos sur le descripteur de périphérique /dev/bus/usb/002/002

[toto@rigel ~]$ udevadm info /dev/bus/usb/002/002 
P: /devices/pci0000:00/0000:00:02.0/usb2/2-3
N: bus/usb/002/002
E: BUSNUM=002
E: DEVNAME=/dev/bus/usb/002/002
E: DEVNUM=002
E: DEVPATH=/devices/pci0000:00/0000:00:02.0/usb2/2-3
E: DEVTYPE=usb_device
E: DRIVER=usb
E: ID_BUS=usb
E: ID_MODEL=USB_Optical_Mouse
E: ID_MODEL_ENC=USB\x20Optical\x20Mouse
E: ID_MODEL_ID=4d64
E: ID_REVISION=0200
E: ID_SERIAL=0461_USB_Optical_Mouse
E: ID_USB_INTERFACES=:030102:
E: ID_VENDOR=0461
E: ID_VENDOR_ENC=0461
E: ID_VENDOR_FROM_DATABASE=Primax Electronics, Ltd
E: ID_VENDOR_ID=0461
E: MAJOR=189
E: MINOR=129
E: PRODUCT=461/4d64/200
E: SUBSYSTEM=usb
E: TYPE=0/0/0
E: USEC_INITIALIZED=201152
[toto@rigel ~]$ 

(En gras ce que nous allons utiliser pour la suite)

Vérifions le contenu du fichier power/control adéquat:

[toto@rigel ~]$ cat /sys/bus/usb/devices/2-3/power/control
auto
[toto@rigel ~]$ 

Sur cette machine, plusieurs distributions sont installées. Cette fois nous sommes dans Manjaro. Pour d'autres distributions où la souris fonctionne correctement, le même fichier contient on.
De ce fait nous essayons

[root@rigel ~]# echo "on" > /sys/bus/usb/devices/2-3/power/control
[root@rigel ~]# 

qui résout instantanément le problème, mais jusqu'au prochain boot seulement

Pour une solution définitive ajoutons la règle

ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="0461", ATTR{idProduct}=="4d64", TEST=="power/control", ATTR{power/control}="on"

nommée usb-power.rules dans /etc/udev/rules.d, puis nous redémarrons le système.

Caramba, ça ne fonctionne pas!

Vérifions ce qu'il en est en lançant la commande:

[root@rigel ~]# udevadm test /bus/usb/devices/2-3  2>&1 | grep writing

Nous redirigeons le canal 2 vers le canal 1 afin que tout soit filtré par grep (plus d'infos sur les redirections dans ce billet), car la plus grande partie du flux de sortie de la commande udevadm transite par le canal 2 (qui n'est pas filtré par grep).

Le résultat en image:


Donc power/control est effectivement mis sur on et la souris fonctionne aussitôt la commande exécutée (bien qu'il s'agisse d'un test).
La règle ajoutée joue bien son rôle. Le mystère reste entier.

Cependant:

[root@rigel ~]# udevadm test /bus/usb/devices/2-3  2>/dev/null | tail -1
run: 'lmt-udev force modules=runtime-pm devices=2-3'
[root@rigel ~]# 

Le responsable ne serait-il pas lmt-udev qui s'exécute en dernier, après application de toutes les règles?
Si lmt-udev est le responsable, pourquoi cette fois la souris ne s'endort elle plus?
Mais sans doute parce-qu'il s'agit d'un test et que lmt-udev n'est pas exécuté.
D'ailleurs nous sommes prévenu:

[root@rigel ~]# udevadm test /sys/bus/usb/devices/2-3 2>/dev/null | head -3
This program is for debugging only, it does not run any program
specified by a RUN key. It may show incorrect results, because
some values may be different, or not available at a simulation run.

D'autre part:

[toto@rigel ~]$ udevadm test /sys/bus/usb/devices/2-3  2>&1 1>/dev/null | grep 'RUN'
RUN 'lmt-udev force modules=runtime-pm devices=%k' /usr/lib/udev/rules.d/99-laptop-mode.rules:3
toto@rigel ~]$ awk 'NR==3' /usr/lib/udev/rules.d/99-laptop-mode.rules
ACTION=="add|remove", SUBSYSTEM=="usb", RUN+="lmt-udev force modules=runtime-pm devices=%k"

/usr/lib/udev/lmt-udev est bien un programme spécifié par une clef RUN (règle 3 de 99-laptop-mode.rules). C'est un script qui appelle /usr/bin/laptop_mode.
Après quelques recherches nous trouvons le fichier /etc/laptop-mode/conf.d/runtime-pm.conf dans lequel nous pouvons mettre notre souris sur une blacklist:

# The list of Device IDs that should not use autosuspend. Use system commands or
# look into sysfs to find out the IDs of your devices.
# Example: AUTOSUSPEND_DEVID_BLACKLIST="046d:c025 0123:abcd"
AUTOSUSPEND_RUNTIME_DEVID_BLACKLIST="0461:4d64"

Dans le même fichier on pourrait à la place blacklister le pilote usb:

# The list of device driver types that should not use autosuspend.  The driver
# type is given by "DRIVER=..." in a device's uevent file.
# Example: AUTOSUSPEND_DEVID_BLACKLIST="usbhid usb-storage"
AUTOSUSPEND_RUNTIME_DEVTYPE_BLACKLIST="usb"


Le problème est résolu!

mercredi 27 mai 2015

grep history

Nous avons l'habitude d'utiliser grep pour filtrer l'output de la commande history.
Mais cette fois, ça ne fonctionne pas:


Pour une raison inconnue grep considère que le flux reçu en input est un fichier binaire.
Bien sûr le problème peut être contourné avec la commande:

[root@rigel ~]# history | grep --binary-file=text restart

Voyons ça en image:


Pourtant auparavant, on n'avait pas besoin de cette option. De plus, cette fois nous sommes root, mais pour un utilisateur ordinaire rien n'est changé: ça fonctionne comme avant!
Quel est ce mystère?
Utilisons file pour déterminer le type du fichier .bash_history:

[root@rigel ~]# file .bash_history 
.bash_history: ISO-8859 text

La clef du mystère est là, car pour toto (utilisateur lambda):

[toto@rigel ~]$ file .bash_history 
.bash_history: UTF-8 Unicode text


Grâce à iconv, nous allons convertir le fichier .bash_history:

[root@rigel ~]# iconv -f ISO-8859-1 -t UTF-8 < .bash_history  > history

et vérifier que le résultat obtenu est celui que nous attendons:


Il reste à copier history dans .bash_history:

[root@rigel ~]# cp history .bash_history 
cp : voulez-vous écraser « .bash_history » ? o
[root@rigel ~]# 

Il faut se déconnecter pour que le changement soit pris en compte: exit puis de nouveau su (sudo -s pour Ubuntu)

Et voilà:



jeudi 21 mai 2015

shebang postgresql (suite)

Dans le billet précédent nous avions écarté l'usage du shebang
#/usr/bin/psql -f
car cette ligne n'est pas  un commentaire pour psql. Elle est envoyée vers le serveur pour traitement, ce qui provoque une erreur.
Tout un coup: une illumination! Et si on commençait par vider le tampon des requêtes ?
Et ça fonctionne!
Modifions le script sbpsql1x du billet précédent et sauvegardons le sous le nom sbpsql4x:

#!/usr/bin/psql -f
\set QUIET
\r
select now()
\echo
\echo 'Sera envoyé au serveur:'
\echo
\p
\echo
\echo 'Procédons:'
\echo
;

Avant de vider le tampon des requêtes (\r), nous avons défini la variable QUIET afin d'éviter le message:

Query buffer reset (cleared).

Vérifions:



Nous pouvons donc utiliser ce shebang. Le problème est que les paramètres passés au script (comme bdtest) sont en fait des arguments pour la ligne de commande psql.

Pour pouvoir passer de vrais paramètres , il est donc préférable d'utiliser exec psql comme dans le script sbpsql5x (obtenu en modifiant le script sbpsql2x):

#!/bin/bash
exec psql bdtest -f "$0"
\set QUIET 
\echo
\echo 'Tampon initial:'
\echo
\p
\echo
\r
select now()
\echo
\echo 'Sera envoyé au serveur:'
\echo
\p
\echo
\echo 'Procédons:'
\echo
;

Nous avons ajouté l'en-tête 
#!/bin/bash
Et comme de toute façon nous allons vider le tampon, la fonction
--() { :; }
est passée à la trappe car il n'est plus besoin que la ligne avec exec soit un commentaire.

Essayons le script:



Adaptons le script dépensepx du billet script psql. Appelons la nouvelle version dépensenpx:

#!/bin/bash
exec psql bdtest -v signification="$1" -f "$0"
\set QUIET
\r
\pset numericlocale
\pset border 2
\pset linestyle u
\pset footer
\set titre 'Dépenses ':signification
\pset title :titre'\n'
\set code_u
\echo
SELECT code_u
FROM utilisations
WHERE signification = :'signification'
\gset
SELECT référence, date_exec::text as date, détails, montant
FROM dépenses
WHERE code_u = :'code_u'
UNION
SELECT 'TOTAL', '', '', sum(montant)
FROM dépenses
WHERE code_u = :'code_u'
order by 1

Ce script fonctionne très bien:



Cependant ce script se termine en même temps que la commande psql.
Si nous voulons apporter certaines améliorations (utilisation de awk,envoi des messages d'erreur vers /dev/null), nous devons l'appeler à partir de dépensenp0x:

fichier=$(mktemp)
exec 7>&1
exec 1>$fichier
exec 6>&2
exec 2>/dev/null
./dépensenpx "$1"
exec 1>&7 7>&-
awk 'NR==6 {y=$0};/TOTAL/ {print y};1' $fichier
exec 2>&6 6>&-

(Toutes les explications se trouvent dans ce billet)

Ce qui donne ceci:



Avant de terminer, précisons que les tables utilisées ici ont déjà été présentées dans ce blog.
Nous avons rappelé les instructions de créations de ces tables dans le billet script psql