code. grind. sleep.

mardi 24 janvier 2012

Compiler dans l'arbre des sources

Je m'occupe actuellement de préparer ma box pour le FOSDEM, et il s'avère qu'il manque le support du DRM (Direct Rendering Manager, le truc pour avoir de l'accélération graphique dans le kernel pour X.org) pour ma carte vidéo. Il s'agit de NetBSD 5.1.1, la version 6 n'aura pas ce manque.

Il faut donc recompiler un noyau pour ajouter cette fonctionnalité, pour faire vite on ne passe pas par build.sh, les tools etc, on compile directement dans l'arbre des sources, montrer comment faire ça est le but de post.

On commence par récupérer les sources sur un serveur CVS près de chez soi :

# export CVS_RSH=ssh
# export CVSROOT="anoncvs@anoncvs.NetBSD.org:/cvsroot"
# cd /usr
# cvs -q -z3 co -P -rnetbsd-5-1-1-RELEASE src

Ensuite, on n'a pas besoin d'avoir une conf particulière dans son /etc/mk.conf, on n'a juste à ajouter les options dans le fichier de conf du kernel et compiler directement par l'intermédiaire de make.

On édite le fichier /usr/src/sys/arch/i386/conf/GENERIC.local, pour y ajouter les lignes suivantes, il est inclus par le fichier GENERIC :

# DRI driver    
i915drm*        at vga?         # Intel i915, i945 DRM driver
mach64drm*      at vga?         # mach64 (3D Rage Pro, Rage) DRM driver
mgadrm*         at vga?         # Matrox G[24]00, G[45]50 DRM driver
r128drm*        at vga?         # ATI Rage 128 DRM driver
radeondrm*      at vga?         # ATI Radeon DRM driver
savagedrm*      at vga?         # S3 Savage DRM driver
sisdrm*         at vga?         # SiS DRM driver
tdfxdrm*        at vga?         # 3dfx (voodoo) DRM driver

On compile le kernel à la main :

# config GENERIC
# cd ../compile/GENERIC
# make depend
# make

On installe le kernel à la main :

# mv /netbsd /netbsd.old
# cp netbsd /
# chmod 444 /netbsd

Pour revenir facilement en arrière en cas de souci, on peut ajouter la ligne suivante dans /boot.cfg, en deuxième position :

menu=Boot old kernel:boot netbsd.old

Il ne reste plus qu'à rebooter sur le nouveau kernel.

Référence : Le guide

On peut faire la même manip pour mettre à jour une partie du système seulement, par exemple lorsqu'une faille de sécurité doit être corrigée. La méthode de compilation dans l'arbre des sources est indiquée dans l'avis.

Plus généralement, la méthode est la suivante, avec l'exemple de ls :

# cd /usr/src/bin/ls
# make USETOOLS=no cleandir
# make USETOOLS=no dependall

Le binaire résultant et sa doc sont prêts dans le répertoire courant, il ne reste qu'à installer :

# make USETOOLS=no install

vendredi 21 octobre 2011

X11 forwarding request failed on channel 0

Quand j'essaye de me logguer sur ma box NetBSD fraichement passé en X.org modular, j'ai ça :

orgrim@serfouette ~ $ ssh rateau 
X11 forwarding request failed on channel 0
Last login: Fri Oct 21 12:20:24 2011 from serfouette.home.orgrim.net

WTF? ça tombe en marche SSH normalement.

Et bien le souci vient de l'échange des magic cookies pour l'authentification entre serveurs X à travers SSH, c'est utilisé par le X11 forwarding et on a besoin de plusieurs choses pour ça :

  • le chemin complet vers xauth sur le client
  • le chemin complet vers xauth sur le serveur
  • Avoir X11Forward yes sur le serveur quand on le demande sur le client (il vaut yes dans le /etc/ssh/ssh_config du client par flemme de taper -X sur la ligne de commande)

Le client est sous Linux, donc pas de soucis le chemin en dur /usr/bin/xauth dans le binaire sshd marche. Par contre pour NetBSD avec du X.org modular, il faut décommenter l'option XAuthLocation dans /etc/ssh/sshd_config et /etc/ssh/ssh_config pour donner le bon chemin vers xauth, comme indiqué en commentaire dans ces deux fichiers :

# If you use xorg from pkgsrc then uncomment the following line.
#  XAuthLocation /usr/pkg/bin/xauth

Enfin, comme le X11Forward est à no par défaut sur NetBSD, je l'ai activé. La vrai solution est de faire ça côté client en utilisant explicitement l'option -X quand on veut ouvrir des fenêtres sur le serveur.

Le pire c'est que Google ne sort rien sur le message "X11 forwarding request failed on channel 0", à part une question sans réponse sur un stackoverflow-like en Russe !

mardi 18 octobre 2011

Quand PostgreSQL n'a plus d'espace disque à manger

Voilà donc une question intéressante, comment se comporte PostgreSQL face à un système de fichier plein ? Un peu d'expérimentation est nécessaire pour se rassurer...

On crée deux systèmes de fichiers de faible taille pour les tests. Le premier stockera PGDATA, ainsi qu'une base de données nommée db_data. Le second sera le tablesapce ts1, dans lequel on créera une base de données db_ts1.

L'objectif est de montrer que seules les transactions modifiant des objets stockés sur des systèmes de fichier plein sont affectées, c'est pourquoi on a besoin de plusieurs tablespaces

Les binaires se trouvent dans /home/pgsql/postgresql-9.0.4, PGDATA dans /home/pgsql/postgresql-9.0.4/data, et le tablespace dans /home/pgsql/postgresql-9.0.4/ts1. On a donc monté et donné la propriété des deux filesystèmes à orgrim, que fera tourner PostgreSQL :

# df -h
...
/dev/mapper/sys-pg1   504M   54M  425M  12% /home/pgsql/postgresql-9.0.4/data
/dev/mapper/sys-pg2   504M   17M  462M   4% /home/pgsql/postgresql-9.0.4/ts1
...

# cd /home/pgsql/postgresql-9.0.4/
# chown orgrim: data ts1
# chmod 700 data ts1

Le cluster est préparé avec l'environnement suivant :

$ env | grep PG
PGPORT=5904
PGDATABASE=postgres
PGDATA=/home/pgsql/postgresql-9.0.4/data
PATH=/home/pgsql/postgresql-9.0.4/bin:$PATH

On lance donc initdb, puis on crée les bases avec le tablespace :

$ initdb
$ psql
psql (9.0.4)
Type "help" for help.

postgres=# CREATE DATABASE db_data;
CREATE DATABASE
postgres=# CREATE TABLESPACE ts1 LOCATION '/home/pgsql/postgresql-9.0.4/ts1';
CREATE TABLESPACE
postgres=# CREATE DATABASE db_ts1 TABLESPACE ts1;
CREATE DATABASE

Ensuite, on se connecte à la base de données db_ts1 et on y crée une base qui permettra de remplir le tablespace ts1 :

$ while ((1)); do psql -c "INSERT INTO t SELECT generate_series(1, 1000) AS i;" db_ts1; done

Au bout, d'un moment le système de fichier du tablespace est plein et tout requête générant des écritures sort en erreur avec un message de cette forme :

ERROR:  could not extend file "pg_tblspc/16385/PG_9.0_201008051/16386/16390": wrote only 4096 of 8192 bytes at block 58438
HINT:  Check free disk space.

Ensuite, on essaye avec le répertoire PGDATA, on crée donc une table dans la base db_data :

$ psql db_data
psql (9.0.4)
Type "help" for help.

db_data=# CREATE TABLE t (i int);
CREATE TABLE

De la même façon, on remplit cette table jusqu'à épuisement de l'espace libre :

$ while ((1)); do psql -c "INSERT INTO t SELECT generate_series(1, 1000) AS i;" db_data; done

Résultat, les requêtes sortent en erreur de la même façon. On a peut-être de la chance ici, le filesystem contenant pg_xlog, les problèmes pourraient être pis.

Il est également possible de remplir le système de fichiers de journaux de transactions, ce qui est problématique du fait que chaque transaction est écrite ici tout tablespace confondu. On vide donc les deux bases :

$ psql db_ts1
psql (9.0.4)
Type "help" for help.

db_ts1=# TRUNCATE t;
TRUNCATE TABLE

$ psql db_data
psql (9.0.4)
Type "help" for help.

db_data=# TRUNCATE t;
TRUNCATE TABLE

Et on place le paramètre checkpoint_segments à 300, valeur démesurée par rapport à la place disponible dans PGDATA.

Après un reload, on refait alors le test de remplissage de la base db_ts1, qui assure que les journaux de transactions seuls remplissent PGDATA.

Le serveur PostgreSQL s'arrête parce qu'il ne peut écrire le journal de transaction :

PANIC:  could not write to file "pg_xlog/xlogtemp.8795": No space left on device
STATEMENT:  INSERT INTO t SELECT generate_series(1, 1000) AS i;
LOG:  server process (PID 8795) was terminated by signal 6: Aborted
LOG:  terminating any other active server processes
WARNING:  terminating connection because of crash of another server process
DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.
HINT:  In a moment you should be able to reconnect to the database and repeat your command.
LOG:  all server processes terminated; reinitializing

Il redémarre illico, mais le problème persiste :

FATAL:  the database system is in recovery mode
LOG:  database system was interrupted; last known up at 2011-10-12 00:01:40 CEST
LOG:  database system was not properly shut down; automatic recovery in progress
LOG:  consistent recovery state reached at 0/5FDB7480
LOG:  redo starts at 0/5AA57D68
LOG:  could not open file "pg_xlog/000000010000000000000074" (log file 0, segment 116): No such file or directory
LOG:  redo done at 0/73FFFF90
LOG:  last completed transaction was at log time 2011-10-12 00:05:11.710639+02
PANIC:  could not write to file "pg_xlog/xlogtemp.8797": No space left on device
LOG:  startup process (PID 8797) was terminated by signal 6: Aborted
LOG:  aborting startup due to startup process failure

Le seul moyen de se sortir de cette situation est d'utiliser les 5% d'espace libre du filesystème réservés au super utilisateur, de baisser la valeur de checkpoint_segments à une valeur évitant le filesystem full, les checkpoints successif supprimeront les fichiers en trop.

Lorsqu'on a plus besoin de 5% réservés, il ne faut pas oublier de remettre la réservation.

Enfin, ce cas le plus critique peut arriver assez facilement lorsqu'on a de l'archivage, si le serveur ne peut plus archiver les fichiers WAL, alors il les conserve, un filesystem full sur un espace d'archivage peut donc être transmis au serveur...

mardi 4 octobre 2011

Passer de X.org natif à modular

X.org est fourni dans le basesys et dans pkgsrc, on appelle le premier « native » et le second « modular » selon la valeur de la variable X11_TYPE que l'on positionne dans son /etc/mk.conf pour signifier à pkgsrc sur lequel linker.

Il s'agit des mêmes versions à peu de choses prêt, et X.org native n'est pas vieux ou non maintenu comme la rumeur voudrait le faire croire. Il est juste en retard parce qu'il suit le cycle de release du basesys alors que modular suit celui de pkgsrc et est tiré vers l'avant par les packages qui en dépendent. Cela peut poser problème lorsqu'on suit la cible mouvante qu'est pkgsrc-current.

La première chose à faire pour passer de native à modular est d'éditer /etc/mk.conf pour changer X11_TYPE, on en profite pour ne plus compiler le native :

MKX11=no 
X11_TYPE=modular

Ensuite, il faut modifier la liste de packages à compiler pour y ajouter soit tout modular en installant meta-pkgs/modular-xorg, soit en n'installant que le nécessaire, ça fait plus cool, dans /usr/pkgsrc/pkgchk.conf (si vous avez suivi les docs ici ou dans le wiki) :

meta-pkgs/modular-xorg-apps
meta-pkgs/modular-xorg-libs
meta-pkgs/modular-xorg-fonts
x11/xf86-input-keyboard
x11/xf86-input-mouse
x11/xf86-input-void
x11/xf86-video-nv
x11/xf86-video-vesa
x11/xf86-video-vga
x11/xf86-video-wsfb
x11/modular-xorg-server

Ensuite on donne ça à manger à pkg_comp ou à son bulk. L'important ici est de tout recompiler pour bien transférer les dépendances de native vers modular : en gros on pète la sandbox, que ça soit de mk/bulk ou pkg_comp et on recommence. Etant passé en mode bulk partiel comme indiqué dans un précédent post, voici comment faire :

1. On vérifie avec mount que la standbox n'est pas montée ni qu'un build tourne (dans ce cas faut le killer), sinon :

# /usr/sandbox/sandbox umount

2. On vérifie que le contenu des mk.conf du système et de la sandbox sont en phase, c'est le seul fichier de la sandbox à sauver

3. On recrée la sandbox :

# rm -rf /usr/sandbox
# cd /usr/pkgsrc/mk/bulk
# sh mksandbox --without-x /usr/sandbox

4. On nettoie les packages déjà compilés, pour forcer leur recompilation, et les fichiers cachés du bulk build :

# cd /usr/pkgsrc/packages/
# mv CVS .CVS
# rm -rf *
# mv .CVS CVS
# cd /usr/pkgsrc
# rm .broken.html .bulk_build_id .bulk_db .bulklock .depends .dependstree .index  .order .start .supports
# pkgclean

5. On lance le bulk build et on attend, il faut prévoir entre 150 et 200 packages supplémentaires à compiler.

Les étapes suivantes sont simples, on sauvegarde tout ce qu'il faut dans /usr/pkg, puis on supprime tous les packages installés :

# cd /usr
# mv pkg pkg.old
# cd /var/db
# mkdir old_pkgdb
# mv pkg pkg.refcount old_pkgdb
# rm -rf pkgin

A partir d'ici, on n'a plus de programmes issus de pkgsrc, en gros par de sudo, vim et autres...

Puis, il faut supprimer les fichiers des sets de X.org, on se base pour cela sur le contenu de /etc/mtree/set.x*. On en arrive donc à un stade où on est dans la même situation qu'après une installation du système sans les sets de X.org natif.

Enfin, on réinstalle tout les packages avec pkgin, qui dans sa version 0.5.0 (du CVS) peut importer une liste de packages au format de pkg_chk, qui se trouve être le même utilisé par le bulk-build, comme de par hasard :

# pkg_add http://pkgsrc.orgrim.net/NetBSD/5.1/amd64/All/pkg_install-20110805.tgz
# mkdir -p /usr/pkg/etc
# cp -r /usr/pkg.old/etc/pkgin /usr/pkg/etc
# ./pkgin up
# ./pkgin im pkgchk.conf

Note : si le pkg_add de pkg_install ne passe pas, essayer avec -f.

et hop, reste plus qu'à reconfigurer les chemins dans /etc/X11/xorg.conf et c'est bon.

vendredi 19 août 2011

Bulk build partiel de pkgsrc

En suivant l'excellent tip de Mr GuiGui2, j'ai pu monter ma petite archi de bulk build personnelle pour fournir du package tout frais à pkgin.

J'ai donc ajouté le bloc magique suivant à mon /etc/mk.conf, qui permet de gérer la présence de commentaires dans pkgchk.conf :

# bulk build config
DEPENDS_TARGET= bulk-install
BATCH=          yes

BULK_PREREQ+=   pkgtools/lintpkgsrc
.if defined(SPECIFIC_PKGS)
PKGLIST!=               awk '$$1 !~ /^\#/ {print $$1}' ${PKGCHK_CONF}
.       for _pkg_ in ${PKGLIST}
HOST_SPECIFIC_PKGS+=    ${_pkg_}
.       endfor
.endif

Pour aller plus loin, j'ai automatisé le process au maximum pour lancer des bulk build réguliers par cron, grâce au script bulk-builder. Ce script remplace do-sandbox-build et do-sandbox-upload, il est également capable de gérer des chemins alternatifs, mettre à jour pkgsrc avant de lancer le bulk.

La procédure pour mettre ça en place est donc :

1. Ajouter le bloc montré plus haut à /etc/mk.conf et y définir PKGCHK_CONF, il s'agit du chemin vers une liste de packages au format "catetgorie/package", un par ligne, qu'on peut automatiquement créer avec pkg_chk -g.

2. Créer la sandbox :

# cd /usr/pkgsrc/mk/bulk
# sh mksandbox --without-x /usr/sandbox

3. Créer et configurer /usr/pkgsrc/mk/bulk/build.conf :

# cd /usr/pkgsrc/mk/bulk
# cp build.conf-example build.conf
# vi build.conf

4. Lancer le bulk build :

# sh bulk-builder -u -R
  • -u demande de cvs up le répertoire /usr/pkgsrc avant de commencer
  • -R demande de ne pas uploader le résultat (les packages)

Enfin, il suffit d'utiliser la ligne suivante pour utiliser les packages avec pkgin, dans /usr/pkg/etc/pkgin/repositories.conf :

file:///usr/pkgsrc/packages/All

jeudi 18 août 2011

Montrer les dépendances avec make dans pkgsrc

Généralement, on peut savoir quelles sont les dépendances d'un package en utilisant make show-depends, mais cela ne montre que les dépendances pour l'installation, les dépendances pour la compilation ne sont pas montrées.

$ cd /usr/pkgsrc/databases/postgresql90-server/
$ make show-depends
postgresql90-client>=9.0.4:../../databases/postgresql90-client

Pour connaître les dépendances selon leur type (installation ou compilation), on peut utiliser la cible show-depends-pkgpaths alliée à la variable DEPENDS_TYPE.

Pour avoir seulement les dépendances de compilation :

$ make DEPENDS_TYPE=build show-depends-pkgpaths
devel/bison
devel/gmake
pkgtools/digest

Pour avoir seulement celles d'installation :

$ make DEPENDS_TYPE=install show-depends-pkgpaths
databases/postgresql90-client

Et enfin pour montrer les deux types, qui est aussi le comportement par défaut :

$ make DEPENDS_TYPE=all show-depends-pkgpaths
databases/postgresql90-client
devel/bison
devel/gmake
pkgtools/digest

$ make show-depends-pkgpaths
databases/postgresql90-client
devel/bison
devel/gmake
pkgtools/digest

Pour plus d'information, le Makefile qui contrôle cette cible est mk/bsd.utils.mk.

samedi 6 août 2011

Le client de la BuildFarm de PostgreSQL dans pkgsrc-wip

Comme j'annonçais précédemment, je contribue deux machines NetBSD à la BuildFarm de PostgreSQL. La compilation ne se fait automagiquement qu'après la configuration du client (écrit en Perl). Il n'est d'ailleurs pas forcément très convi à installer, c'est pourquoi je l'ai packagé pour pkgsrc : http://pkgsrc.se/wip/pgbuildfarm.

En espérant qu'il soit ajouté à l'arbre officiel...

Voici la configuration pour lancer des builds sur NetBSD, dans /usr/pkg/etc/pgbuildfarm/build-farm.conf, en commençant par le chemin du miroir du dépôt Git :

# Modifier dans %conf
scmrepo => '/usr/pgbuildfarm/pgsql-base.git',

Le client est destiné à être lancé par cron, connu pour son environnement light, c'est pourquoi les paramètres d'environnement doivent être adaptés :

  • Le make GNU s'appelle gmake chez nous
  • Pas mal de programmes proviennent de pkgsrc, il faut donc que le client ait /usr/pkg/bin dans son PATH, et puisse trouver les bibliothèques issues des packages.
make => 'gmake',
aux_path => "/usr/pkg/bin",

build_env =>
{
    PATH => "/usr/pkg/bin:$ENV{PATH}",
    LD_LIBRARY_PATH => "/usr/pkg/lib",
},

config_env =>
{
    CC => 'gcc',
    PATH => "/usr/pkg/bin:$ENV{PATH}",
    LD_LIBRARY_PATH => "/usr/pkg/lib",
},

Enfin le plus important, les options du configure, la plupart nécessitent des packages supplémentaires comme python ou la libxml. Ce qui est primordial ici est d'utiliser le « template » NetBSD :

config_opts =>
[qw(
    --enable-cassert
    --enable-debug
    --enable-nls
    --enable-integer-datetimes
    --with-perl
    --with-python
    --with-tcl
    --with-krb5
    --with-includes=/usr/include/krb5:/usr/pkg/include
    --with-libraries=/usr/pkg/lib
    --with-openssl
    --with-template=netbsd
    --enable-thread-safety
)],

Pour toutes ces options, les packages suivants ont été installés :

  • devel/bison
  • devel/flex
  • lang/python26 (et pkgtools/pkg_alternatives pour avoir le lien python)
  • lang/perl5
  • lang/tcl
  • textproc/libxml2
  • textproc/libxslt
  • devel/readline

P.S. : Il n'y a que les particularités de NetBSD décrites ici, en complément du wiki de PostgreSQL.

vendredi 5 août 2011

Could not open relation with oid N

On peut parfois trouver cet étrange message d'erreur dans les traces de PostgreSQL (N étant un nombre) ou lors de l'exécution d'une requête :

ERROR:  could not open relation with OID N

Si on recherche ce message dans les mailing-lists du projet, on peut facilement conclure que la base de données est corrompue, qu'il y a des problèmes matériels et que la sécurité des données est en péril. Et bien, ce n'est pas forcément le cas : obtenir ce message peut être tout à fait normal.

Pour démontrer cela, on a besoin d'une table :

$ createdb test
$ psql test
psql (9.0.4)
Type "help" for help.

test=# CREATE TABLE truc AS SELECT generate_series(0, 5) AS i;
SELECT 6
test=#

On lance une session qui bloquerait un DROP de cette table, pour cela on pose un verrou exclusif, le mode « ExclusiveLock » ne laisse passer que les lectures (c'est important pour la suite) :

$ psql test
psql (9.0.4)
Type "help" for help.

test=# BEGIN;
BEGIN
test=# LOCK TABLE truc IN EXCLUSIVE MODE;
LOCK TABLE
test=#

On laisse cette transaction « ouverte », avec le verrou posé et on lance une session pour supprimer la table :

$ psql test
psql (9.0.4)
Type "help" for help.

test=# BEGIN;
BEGIN
test=# DROP TABLE truc;

L'ordre SQL DROP TABLE ne rend pas la main, cette deuxième session attend le verrou « AccessExclusiveLock », qui est le plus restrictif, sur la table pour pouvoir la supprimer. La page http://wiki.postgresql.org/wiki/Loc... fournie une requête montrant les dépendances entre requêtes du point de vue du verrouillage. Dans ce cas, elle donne le résultat suivant :

 waiting_locktype | waiting_table |  waiting_query   |    waiting_mode     | waiting_pid | other_locktype | other_table |      other_query      |  other_mode   | other_pid | other_granted 
------------------+---------------+------------------+---------------------+-------------+----------------+-------------+-----------------------+---------------+-----------+---------------
 relation         | truc          | DROP TABLE truc; | AccessExclusiveLock |       25632 | relation       | truc        | <IDLE> in transaction | ExclusiveLock |     24217 | t

On lance une troisième session, avec un SELECT sur notre table :

$ psql test
psql (9.0.4)
Type "help" for help.

test=# SELECT * FROM truc;

L'ordre SELECT ne rend pas la main, cette troisième session se met à attendre le DROP TABLE et la première session. C'est d'ailleurs le DROP TABLE qui bloque réellement le SELECT, car la première session à verrouillé la table en lecture seule :

 waiting_locktype | waiting_table |    waiting_query    |    waiting_mode     | waiting_pid | other_locktype | other_table |      other_query      |     other_mode      | other_pid | other_granted 
------------------+---------------+---------------------+---------------------+-------------+----------------+-------------+-----------------------+---------------------+-----------+---------------
 relation         | truc          | SELECT * FROM truc; | AccessShareLock     |       28629 | relation       | truc        | DROP TABLE truc;      | AccessExclusiveLock |     25632 | f
 relation         | truc          | DROP TABLE truc;    | AccessExclusiveLock |       25632 | relation       | truc        | <IDLE> in transaction | ExclusiveLock       |     24217 | t
 relation         | truc          | SELECT * FROM truc; | AccessShareLock     |       28629 | relation       | truc        | <IDLE> in transaction | ExclusiveLock       |     24217 | t
 relation         | truc          | DROP TABLE truc;    | AccessExclusiveLock |       25632 | relation       | truc        | SELECT * FROM truc;   | AccessShareLock     |     28629 | f
(4 rows)

On libère la première session :

test=# ROLLBACK;
ROLLBACK
test=#

Le DROP TABLE passe, et le SELECT continue d'attendre :

 waiting_locktype | waiting_table |    waiting_query    |  waiting_mode   | waiting_pid | other_locktype | other_table |      other_query      |     other_mode      | other_pid | other_granted 
------------------+---------------+---------------------+-----------------+-------------+----------------+-------------+-----------------------+---------------------+-----------+---------------
 relation         | truc          | SELECT * FROM truc; | AccessShareLock |       28629 | relation       | truc        | <IDLE> in transaction | AccessExclusiveLock |     25632 | t

On voit que le SELECT attend la transaction qui a lancé le DROP TABLE. Même si le DROP TABLE est terminé, son effet ne sera connu des transactions concurrentes seulement ou moment du commit ou rollback, parce qu'on utilise le niveau d'isolation des transaction « read committed » (par défaut). Il n'y a pas de « UNLOCK » sur les tables dans PostgreSQL, il faut attendre la fin de la transaction pour que les verrous soient libérés, du moins lorsqu'on n'utilise pas de savepoints.

Maintenant, on valide le DROP TABLE, avec l'ordre COMMIT. Le SELECT termine en erreur, on voit alors le message dans les logs :

ERROR:  could not open relation with OID 17366 at character 15
STATEMENT:  SELECT * FROM truc;

Lorsque le SELECT n'est plus bloqué par le verrou, il ne peut accéder pas à la table car elle n'existe plus. Le message n'est pas très explicite parce que la requête est en cours d'exécution : le moteur a déjà terminé le travail de parsing et de planification, il ne travaille qu'avec les OID qu'il a récupéré du catalogue système à ce moment là.

Dans ce cas précis, obtenir ce message n'est un problème de corruption de la base ou du catalogue système.

Deux machines NetBSD dans la buildfarm de PostgreSQL

Il y a quelques temps, j'avais remarqué que seules deux machines NetBSD étaient présentes dans la buildfarm de PostgreSQL sur du powerpc et du mips. Pas de machines i386 et amd64, qui sont pourtant les architectures phares du TIER 1, et ce depuis plus d'un an.

L'affront est désormais réparé et deux nouveaux drapeaux oranges flottent fièrement dans les prés de la buildfarm, fournies par votre serviteur :

  • panther : NetBSD 5.1 gcc 4.1.3 i386
  • smilodon : NetBSD 5.1 gcc 4.1.3 amd64

Les machines tournent maintenant depuis quelques temps, sans aucun soucis.

jeudi 4 août 2011

pitrery : des scripts pour faciliter la sauvegarde PITR

Gérer une sauvegarde PITR (Point-In-Time Recovery), permettant de revenir à un point précis dans le temps sur un serveur PostgreSQL, n'est pas forcément évident à mettre en place et ensuite à gérer au quotidien : la restauration en cas de problème peut même prendre beaucoup de temps à mettre en place, avec des possibilités d'erreurs non négligeables. C'est d'autant plus stressant que la pression monte quand on en a besoin...

C'est pourquoi, dans le cadre de mon travail, j'ai écrit un ensemble de scripts shell en Bash (ce qui permet d'utiliser des commandes qui sont déjà sur une installation classique de Linux) pour faciliter ce travail. Le code est hébergé sur github : https://github.com/dalibo/pitrery. La version 1.0 vient d'être taguée. C'est encore un peu brut de décoffrage niveau documentation, mais les scripts ont été testés dans tous les sens, donc ce jeu d'outils devrait être utilisable en production sans trop de difficultés.

Il y a un Makefile GNU, à l'ancienne, les fichiers de configuration sont plein de commentaires pour expliquer et il y des « usage » pour chaque script à base de -? ou -h qui devraient parler à ceux qui savent déjà gérer du PITR.

mardi 5 juillet 2011

Configuration réseau pour virtualiser chez OVH

Sur mon serveur chez OVH, j'ai un ensemble de machines virtuelles KVM et (bientôt) de conteneurs LXC. Pour fournir du réseau à tout ce petit monde, j'utilise de l'IPv4 et de l'IPv6, voici comment c'est configuré.

Pour l'IPv4, on a un nombre limité d'IP publiques parce que ça vaut de la thune et que ça va être de plus en tendu de multiplier les adresses, il nous faut un réseau privé (beurk), du NAT (rebeurk) et des redirections à base d'iptables (re-rebeurk). Il nous faut surtout un bridge, c'est une Debian donc ça se passe dans /etc/network/interfaces :

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet manual

auto br0
iface br0 inet static
	bridge_ports eth0
	bridge_fd 0
	bridge_maxwait 0
	address 188.165.205.xxx
	netmask 255.255.255.0
	network 188.165.205.0
	broadcast 188.165.205.255
	gateway 188.165.205.254
	post-up iptables -t nat -A POSTROUTING -s 10.42.0.0/24 -o br0 -j SNAT --to 188.165.205.xxx
	pre-down iptables -t nat -D POSTROUTING -s 10.42.0.0/24 -o br0 -j SNAT --to 188.165.205.xxx

iface br0 inet6 static
	address 2001:41D0:xxxx:96ce::1
	netmask 64

auto br0:0
iface br0:0 inet static
	address 10.42.0.1
	netmask 255.255.255.0

Pour le bridge, on met l'IPv4 publique et une règle iptable pour NAT-er ce qui sort. Le réseau privé est 10.42.0.0/24, on peut bien sûr choisir ce qu'on veut dans ce que fourni la RFC 1918.

Pour l'IPv6, chez OVH c'est bizarre : il fournissent un prefix 64 bit et avec une route accessible sur le /56 incluant, mais pas à partir du /64. Il faut donc ruser de la façon suivante niveau routage :

  • On assigne l'IPv6 avec le prefix /64, c'est plus propre, sinon le kernel se plaint avec /56 :
# ip -6 addr add 2001:41D0:xxxx:96ce::1/64 dev br0
  • On met une route pour atteindre la gateway dans le /56 :
# ip -6 route add 2001:41d0:xxxx:96ce::/56 dev br0
  • On met la route par défaut :
# ip -6 route add default via 2001:41d0:xxxx:96ff:ff:ff:ff:ff

Pour avoir cette configuration appliquée automatiquement, il faut créer /etc/network/if-up.d/ovh-ipv6-route :

#!/bin/sh

if [ "$IFACE" = "br0" ]; then
	ip -6 route add 2001:41d0:2:96ce::/56 dev br0
	ip -6 route add default via 2001:41d0:2:96ff:ff:ff:ff:ff
fi

Et pour l'arrêt, /etc/network/if-down.d/ovh-ipv6-route :

#!/bin/sh

if [ "$IFACE" = "br0" ]; then
	ip -6 route del default via 2001:41d0:2:96ff:ff:ff:ff:ff
	ip -6 route del 2001:41d0:2:96ce::/56 dev br0
fi

Il faut ensuite activer le forward entre interfaces, sinon pas de routage vers les VM, dans /etc/sysctl.conf :

net.ipv6.conf.all.forwarding=1

Enfin, le routage chez OVH ne permet pas d'intercaler un routeur pour découper le /64, il faut donc ruser avec du proxy NDP (Neighbor Discovery Protocol) :

  • On active le proxy NDP, dans /etc/sysctl.conf :
net.ipv6.conf.all.proxy_ndp=1
  • On ajoute les IP à proxiser, celle de la gateway et chacune des IP fournies aux VM :
# ip neigh add proxy 2001:41d0:xxxx:96ff:ff:ff:ff:ff dev br0
# ip neigh add proxy 2001:41d0:xxxx:96ce::2 dev br0

En cas de reboot d'une VM, il faut d'abord virer ses IP du proxy, puis les remettre, sinon le kernel de la VM se plein d'une duplication d'adresse :

# ip neigh del proxy 2001:41d0:xxxx:96ce::2 dev br0
-> reboot de la VM
# ip neigh add proxy 2001:41d0:xxxx:96ce::2 dev br0

lundi 4 juillet 2011

NetBSD en KVM

Comme je viens d'investir dans un (le 16G), je me suis dis qu'avoir quelques machines NetBSD pour servir la bonne cause ça serait bien cool.

En fait, j'ai deux besoins :

  • Avoir un dépôt de paquet et de quoi fait des bulk build réduits de pkgsrc pour me permettre de n'utiliser uniquement l'excellent pkgin
  • Fournir des machines à la Build Farm de PostgreSQL, parce qu'il n'y a même pas de machine NetBSD en i386 et en amd64 (seulement powerpc et mips)

J'ai donc décidé de monter 2 machines NetBSD en KVM sur ma grosse box Debian, qui fait tourner ça grâce à KVM.

Pour l'installation, il y a 2 possibilités, soit on fait du VNC, soit on redirige la sortie VGA dans du curses. Pour le disque j'ai choisi de poser directement les données sur des volumes logiques, dans ce cas, il faut désactiver le cache ce qui permet une infime perte de puissance en I/O.

Il faut commencer par récupérer l'ISO d'installation :

wget ftp://ftp.fr.netbsd.org/pub/NetBSD/iso/5.1/amd64cd-5.1.iso

Ensuite, créer le volume logique (on a choisi de donner 50 Go) et lancer l'installation, en curses ça passe nickel, il faut choisir d'utiliser la console serie :

kvm -drive file=/dev/system/kvm-nb64-d1,cache=none \
    -m 1024 \
    -net nic,model=e1000 -net tap \
    -name nb64 \
    -curses \
    -cdrom /home/orgrim/netbsd/amd64cd-5.1.iso \
    -boot d \
    -k fr

Une fois installé, on lance la commande suivante dans un screen, on demande à KVM de fournir l'accès console en série dans un fichier, ce qui permet d'avoir la console QEMU disponible directement dans le screen :

kvm -nographic \
    -drive file=/dev/system/kvm-nb64-d1,cache=none \
    -m 1024 \
    -net nic,model=e1000,macaddr=DE:AD:BE:EF:37:D1 -net tap \
    -name nb64 \
    -boot c \
    -serial unix:/tmp/nb64.sock,server,nowait

Note: merci de changer la MAC de l'interface réseau, c'est utilisé en prod chez moi :)

Enfin, il est important de démarrer le noyau NetBSD sans ACPI ni SMP (en mettant le defaut à 4 dans /boot.cfg)

Pour accéder à la machine en console série :

minicom -D unix#/tmp/nd64.sock

Si on a oublié de choisir la console série, on peut l'activer de cette façon :

  1. Booter avec -curses -k fr à la place de -nographic
  2. Lancer la commande suivante pour activer la console série :
# installboot -v -o console=com0,speed=19200 /dev/rwd0a /usr/mdec/bootxx_ffsv1
File system:         /dev/rwd0a
Primary bootstrap:   /usr/mdec/bootxx_ffsv1
Boot options:        timeout 5, flags 0, speed 19200, ioaddr 0, console com0

mercredi 29 juin 2011

Rediriger stdout/stderr depuis un script avec du pipe

Pour rediriger stdout/stderr à l'interieur vers l'entrée standard d'un commande, il faut utiliser exec et du sous-shell. Cette astuce est un bashisme a priori.

L'objectif est de renvoyer tous les messages du script dans syslog sans mettre de redirection sur la ligne de commande. Le principe général est :

exec FD> >(COMMAND)
  • FD est le numéro du file descriptor, 1 pour stdout, 2 pour stderr
  • COMMAND est la commande a exécuter, elle doit bien sûr lire les données en entrée.

Un plus gros exemple :

SYSLOG="no"
 
# Load configuration file
CONFIG=/etc/myconfig.conf
if [ -f "$CONFIG" ]; then
    . $CONFIG
fi
 
# Redirect output to syslog if configured
if [ "$SYSLOG" = "yes" ]; then
    SYSLOG_FACILITY=${SYSLOG_FACILITY:-local0}
    SYSLOG_IDENT=${SYSLOG_IDENT:-postgres}
 
    exec 1> >(logger -t ${SYSLOG_IDENT} -p ${SYSLOG_FACILITY}.info)
    exec 2> >(logger -t ${SYSLOG_IDENT} -p ${SYSLOG_FACILITY}.err)
fi

avec dans la configuration :

  • SYSLOG : mettre à yes ou no pour activer
  • SYSLOG_FACILITY : ou envoyer
  • SYSLOG_IDENT : avec quel tag

L'exemple est pris d'un de mes scripts d'archivage pour PostgreSQL, ce qui permet de logguer dans syslog sans mettre de pipe dans archive_command (ce qu'il ne faut pas faire parce que ça casse : le code retour donné à PostgreSQL est celui de la dernière commande de la chaîne de pipe)

mardi 21 juin 2011

Combiner des PDF en un seul

Pour combiner des pdf en un seul, on peut essayer pdfjoin fournit par le projet pdfjam. En attendant que les 250 Mo de dépendances (Latex principalement) s'installent, on peut utiliser ghostscript :

gs -sDEVICE=pdfwrite -dNOPAUSE -dQUIET -dBATCH -sOutputFile=../combined_doc.pdf *.pdf

Merci à http://www.perlmonks.org/?node_id=4...

dimanche 10 avril 2011

Installation de cgit

Cgit est un programme CGI écrit en C qui permet d'obtenir une interface web de navigation pour un ensemble de dépôts git.

Cgit a besoin de OpenSSL, le paquet Debian est libssl-dev.

On récupère la version de stable de cgit :

$ wget http://hjemli.net/git/cgit/snapshot/cgit-0.9.tar.bz2
$ tar xjf cgit-0.9.tar.bz2

Il faut d'abord configurer l'installation en éditant le fichier Makefile. L'interface a besoin d'un endroit accessible par un serveur web, nous allons prendre /var/www/orgrim.net/git. On configure donc les chemins dans le Makefile, de cette façon :

CGIT_SCRIPT_PATH = /var/www/orgrim.net/git
prefix = /usr/local/cgit

Les binaires iront donc dans /var/www/orgrim.net/git/, les bibliothèques et autres fichiers dans une arborescence sous /usr/local/cgit, le fichier de configuration, sera /etc/cgitrc, la valeur par défaut.

Ensuite, il faut suivre les instructions données dans le README. On doit récupérer le dépôt git de git :

$ make get-git

Puis on compile et on installe :

$ make
$ sudo make install

On configure ensuite un virtual host Apache :

$ cat /etc/apache2/sites-enabled/git.orgrim.net 
<VirtualHost *:80>
	ServerAdmin webmaster@localhost
	ServerName git.orgrim.net

	DocumentRoot /var/www/orgrim.net/git
	<Directory /var/www/orgrim.net/git>
        	Addhandler cgi-script .cgi
        	DirectoryIndex cgit.cgi
		Options +FollowSymLinks +ExecCGI
		AllowOverride None
		Order allow,deny
		allow from all

        	RewriteEngine On
        	RewriteBase /
        	RewriteCond %{REQUEST_FILENAME} !-f
        	RewriteCond %{REQUEST_FILENAME} !-d
        	RewriteRule (.*) cgit.cgi/$1
        	RewriteRule ^cgit.cgi$  cgit.cgi/
	</Directory>

   	## Logs
	# Possible values include: debug, info, notice, warn, error, crit,
	# alert, emerg.
	LogLevel warn

	ErrorLog /var/log/apache2/orgrim.net/git_error.log
	CustomLog /var/log/apache2/orgrim.net/git_access.log combined

</VirtualHost>

On peut aller voir le site, il doit afficher une page générée par cgit montrant qu'aucun dépôt n'est configuré pour l'affichage.

Il faut maintenant créer le fichier de configuration, /etc/cgitrc en recopiant la partie exemple du fichier source de la doc cgit-0.9/cgitrc.5.txt.

Ici, l'important est de définir le paramètre de configuration pour matcher avec la configuration des rewrite rules du virtual host :

virtual-root=/

Enfin, les chemins vers la CSS et le logo sont à adapter, par rapport au document root du virtual host :

css=/cgit.css
logo=/cgit.png

Le reste de la configuration est assez facile à adapter, pour pouvoir ajouter ses dépôts.

jeudi 7 avril 2011

Retrouver un trigger à partir du nom de la fonction associée

Ici encore, tout est dans le catalogue système de PostgreSQL, il suffit de regarder dans la table pg_trigger.

Avec les jointures qui vont bien, on peut obtenir les informations intéressantes :

SELECT nspname AS schema, relname AS TABLE, tgname AS TRIGGER, proname AS FUNCTION
FROM pg_trigger t
  JOIN pg_class c ON (t.tgrelid = c.oid)
  JOIN pg_namespace n ON (n.oid = c.relnamespace)
  JOIN pg_proc p ON (p.oid = t.tgfoid);

En ajoutant une clause WHERE, on peut facilement retrouver la table associée au trigger :

SELECT nspname AS schema, relname AS TABLE, tgname AS TRIGGER, proname AS FUNCTION
FROM pg_trigger t
  JOIN pg_class c ON (t.tgrelid = c.oid)
  JOIN pg_namespace n ON (n.oid = c.relnamespace)
  JOIN pg_proc p ON (p.oid = t.tgfoid)
WHERE proname = 'mafonction';

jeudi 24 mars 2011

Insérer des tabulations dans sa ligne de commande bash

Il y a longtemps que je me demandais comment faire ça, sans prendre la peine de rechercher ou lire le man. C'est chose faite, pour insérer une tabulation, il faut contourner la complétion de commandes avec le combo suivant :

C-v TAB

C'est simple, mais ça ne s'invente pas.

Pour le coup, c'est utile quand on veut voir un fichier de configuration sans les commentaires, par exemple postgresql.conf :

grep -Ev '^(( |        )*#|$)' postgresql.conf

On doit donc taper une tabulation dans l'expression régulière : on affiche toutes les lignes qui ne satisfont pas la condition « tout ce qui commence par 0 ou plusieurs espaces ou tabulations suivi d'un dièse, ou une ligne vide ».

mardi 15 mars 2011

Voir tous les champs d'une table

Avec PostgreSQL, on peut utiliser la requête suivante pour obtenir la taille de chacune des bases de données d'un cluster :

SELECT datname AS base,
  pg_size_pretty(pg_database_size(oid)) AS taille
FROM pg_database;

Qui donne par exemple :

         base          | taille  
-----------------------+---------
 template0             | 5273 kB
 postgres              | 5385 kB
 redmine               | 9057 kB
 roundcube             | 9153 kB
 template1             | 5369 kB
 dotclear              | 6833 kB
 dspam                 | 204 MB
 exim                  | 5649 kB
 (8 rows)

Mais comment est-ce possible ? on a appliqué la fonction pg_database_size() sur une colonne appelée oid, alors que la définition de pg_database est la suivante :

postgres=# \d pg_database
    Table "pg_catalog.pg_database"
    Column     |   Type    | Modifiers 
---------------+-----------+-----------
 datname       | name      | not null
 datdba        | oid       | not null
 encoding      | integer   | not null
 datcollate    | name      | not null
 datctype      | name      | not null
 datistemplate | boolean   | not null
 datallowconn  | boolean   | not null
 datconnlimit  | integer   | not null
 datlastsysoid | oid       | not null
 datfrozenxid  | xid       | not null
 dattablespace | oid       | not null
 datacl        | aclitem[] | 
Indexes:
    "pg_database_datname_index" UNIQUE, btree (datname), tablespace "pg_global"
    "pg_database_oid_index" UNIQUE, btree (oid), tablespace "pg_global"
Tablespace: "pg_global"

Il n'y a pas de colonne oid dans cette table ! Et pourtant... Cette colonne est tout simplement cachée, mais elle existe bel et bien. Pour vérifier, on recherche donc dans le catalogue système la liste des champs de la table :

SELECT attname, typname, attnum
FROM pg_attribute a
  JOIN pg_class c ON (c.oid = a.attrelid)
  JOIN pg_type t ON (t.oid = a.atttypid)
WHERE relname = 'pg_database';

Qui donne :

    attname    | typname  | attnum 
---------------+----------+--------
 tableoid      | oid      |     -7
 cmax          | cid      |     -6
 xmax          | xid      |     -5
 cmin          | cid      |     -4
 xmin          | xid      |     -3
 oid           | oid      |     -2
 ctid          | tid      |     -1
 datname       | name     |      1
 datdba        | oid      |      2
 encoding      | int4     |      3
 datcollate    | name     |      4
 datctype      | name     |      5
 datistemplate | bool     |      6
 datallowconn  | bool     |      7
 datconnlimit  | int4     |      8
 datlastsysoid | oid      |      9
 datfrozenxid  | xid      |     10
 dattablespace | oid      |     11
 datacl        | _aclitem |     12
(19 rows)

La colonne attnum de pg_attribute indique la position de la colonne, on remarque ainsi qu'il y a sept colonnes avec une position négative. Ces colonnes ne sont pas montrés par la commande \d de psql, ce sont les colonnes système de la table (plus d'information sur ce qu'elles représentent dans la doc)

En plus, on a encore utilisé cette colonne oid pour la retrouver dans la table... Maintenant, on peut être rassuré sur le catalogue système, ce n'est qu'un ensemble de tables, auxquelles on accède en SQL standard...

pg_class.reltuples n'est qu'une approximation du nombre de lignes

Dans le catalogue de PostgreSQL, qui donne plein d'information sur le cluster et la base de données, la table pg_class regroupe les informations sur les tables, les séquences, les index, tout ce qui contient des colonnes, appelé relation. L'un des champs disponibles est reltuples, il contient le nombre approximatif de lignes dans une relation. C'est approximatif, ainsi ce n'est pas un donnée sure... La preuve par l'exemple.

On crée un table avec une colonne de type entier :

db=# CREATE TABLE truc (i int);
CREATE TABLE

On retrouve ensuite notre table ainsi que la valeur de reltuples dans le catalogue système :

db=# SELECT relname, reltuples FROM pg_class WHERE relname = 'truc';
 relname | reltuples 
---------+-----------
 truc    |         0
(1 row)

La table est vide, et reltuples est à zéro. On insère ensuite quelques lignes dans cette table :

db=# INSERT INTO dalibo.truc VALUES (1),(2),(3),(4);
INSERT 0 4

On regarde la valeur de reltuples pour la table après cette insertion :

db=# SELECT relname, reltuples FROM pg_class WHERE relname = 'truc';
 relname | reltuples 
---------+-----------
 truc    |         0
(1 row)

reltuples n'est pas à jour, il vaut toujours zéro alors que la table contient 4 lignes !

Comme indiqué dans la documentation, reltuples est mis à jour lors d'un ANALYSE ou d'autres opérations de type DDL :

db=# ANALYZE truc;
ANALYZE
db=# SELECT relname,reltuples FROM pg_class WHERE relname = 'truc';
 relname | reltuples 
---------+-----------
 truc    |         4
(1 row)

Et voilà, le passage du ANALYSE à permis de mettre à jour la valeur de reltuples pour la table.

Conclusion, si on désire supprimer une table parce qu'elle est vide, le moyen sûr est de compter le nombre de lignes dans la table et non pas se baser sur la valeur de reltuples.

samedi 12 mars 2011

pkgin adventures : conflit résolu

Il y a quelques jours, il a été décidé de remplacer libungif par giflib dans pkgsrc. Pour éviter de mixer les deux et donc avoir des problèmes, les deux paquets se déclarent mutuellement en conflit. A partir de maintenant la dépendance par défaut est sur giflib, ce qui a donc fait que ma mise à jour (pkg_chk dans un pkg_comp) a tellement buté dessus que j'ai décidé qu'il serait plus simple de repartir d'un chroot pkg_comp tout neuf...

Même si j'avais oublié de retirer libungif de mon /usr/pkgsrc/pkgchk.conf au début, j'ai bien obtenu un dépôt tout neuf pour pkgin.

Et là, la magie de pkgin a opéré :

# pkgin up
processing local summary...
updating database: 100%
downloading pkg_summary.bz2:    0Bbps 100%
processing remote summary (file:///usr/pkgsrc/packages/amd64/All)...
updating database: 100%

# pkgin fug
calculating dependencies... done.
giflib-4.1.6 (to be installed) conflicts with installed package libungif-4.1.4nb1.
proceed ? [y/N] n

# pkgin srd libungif
local reverse dependency tree for libungif
        feh-1.3.4nb8
        giblib-1.2.4nb9
        python-mode-1.0nb1
        php-mode-1.4.0nb1
        mplayer-1.0rc20100913nb5
        emacs-23.2nb4
        imlib2-1.4.2nb6

# pkgin rm libungif
8 packages to delete: mplayer-1.0rc20100913nb5 php-mode-1.4.0nb1 python-mode-1.0nb1
 feh-1.3.4nb8 emacs-23.2nb4 giblib-1.2.4nb9 imlib2-1.4.2nb6 libungif-4.1.4nb1
proceed ? [y/N] y
....

# pkgin in feh emacs php-mode python-mode mplayer
...

# pkgin fug
calculating dependencies... done.

21 packages to be upgraded: epdfview-0.1.7nb10 mercurial-1.8 scmgit-base-1.7.3.5
scmgit-docs-1.7.3.5 poppler-glib-0.16.2 libgnome-2.32.0nb2 libgnomeui-2.24.4nb2
poppler-glib-0.16.2 poppler-utils-0.16.2 t1lib-5.1.2nb1 gtk2+-2.22.1nb1
tex-dvipdfm-0.13.2dnb3 curl-7.21.3 glib2-2.26.1nb2 gnutls-2.10.4 libksba-1.1.0
dialog-1.1.20110118 libidn-1.19 luatex-0.65.0nb1 web2c-2010nb6 poppler-0.16.2

21 packages to be installed: poppler-0.16.3 dialog-1.1.20110302 libidn-1.20
luatex-0.65.0nb2 web2c-2010nb7 curl-7.21.4 glib2-2.28.2 gnutls-2.10.5nb1 libksba-1.2.0
gtk2+-2.24.1 tex-dvipdfm-0.13.2dnb4 poppler-glib-0.16.3 libgnome-2.32.1
libgnomeui-2.24.5 poppler-glib-0.16.3 poppler-utils-0.16.3 t1lib-5.1.2nb2
epdfview-0.1.7nb11 mercurial-1.8.1 scmgit-base-1.7.4.1 scmgit-docs-1.7.4.1
(40M to download, 292M to install)

proceed ? [y/N] y
...

Et hop, résolution du conflit à la main, certes, mais très facilement et avec un seul outil. Sans pkgin, j'aurais du itérer à coup de pkg_info -R, pkg_delete et pkg_chk -ub... Parce pkgin ressort tout l'arbre des dépendances :

 # pkg_info -R giflib
Information for giflib-4.1.6:

Required by:
imlib2-1.4.2nb7
emacs-23.2nb5
mplayer-1.0rc20100913nb6


# pkgin srd giflib
local reverse dependency tree for giflib
        feh-1.3.4nb8
        giblib-1.2.4nb9
        python-mode-1.0nb1
        php-mode-1.4.0nb1
        mplayer-1.0rc20100913nb6
        emacs-23.2nb5
        imlib2-1.4.2nb7

- page 1 de 2