Introducción a Linux/Chuletario

Chuletario de comandos para administrar Linux editar

Ejecutar un script desde el propio directorio donde se localiza el script editar

Es frecuente tener que ejecutar scripts que hagan referencia a otros ficheros (csv, txt, conf,...) relativos al mismo. Conviene entonces cambiar el directorio activo (cwd) antes de ejecutar nada. Puesto que a veces el script debe ser ejecutado simultáneamente desde cron, ssh, línea de comandos, otros script,... al mover el mismo nos vemos obligados a cambiar todos los anteriores. Para solucionarlo ejecutamos al comienzo del script el siguiente comando:


cd $(dirname $BASH_SOURCE) 
Notas: $BASH_SOURCE devuelve la ruta completa respecto a /.
       dirname extrae el directorio base.

Desviar la salida estándar de un script editar

Utilizamos exec justo como segunda línea del script. Ej:

#!/bin/sh
exec  1>>/var/log/$(basename ${0}).$(whoami).log 2>&1 
Notas:
# $(basename ${0}) extrae el nombre del script excluyendo los prefijos del path.
# $(whoami) añade el nombre de usuario. Esto evita problemas de permisos.
# >> indica "añadir al final del fichero".
# 2>&1 desvia la salida estándar de errores a la salida estándar.

Concatenar la salida de varios comandos en Bash editar

Supongamos que queremos enviar un email concatenando varias campos. Algunos de estos campos están generados a partir de un fichero plantilla, otros a partir del valor de una variable del propio script u otras fuentes. Queremos que enviar la salida conjunta de todos estos comandos a la entrada estándar del programa de envio de correo ssmtp.

Intuitivamente podríamos intentar algo similar a:

 ORIGEN="$(hostname)@miDominio.org"                           
 DESTINO="alarmas@miDominio.org" 
 echo "Subject: Se detect'o un problema .... "                | \
 echo "to: Alarmas MiDominio.org"                             | \
 echo ""                                                      | \
 echo "Errores Detectados:"                                   | \
 echo "${ERRORES}"                                            | \
 echo ""                                                      | \
 echo "/var/run/estado:"                                      | \
 cat  /var/run/estado                                         | \
 echo ""                                                      | \
 echo "e-mail generado autom'aticamente por ${0}@$(hostname)" | \
 echo ""                                                      | \
 /usr/sbin/ssmtp -f ${ORIGEN} -F "Host $(hostname)" ${DESTINO}

Sin embargo el ejemplo anterior falla ya que ssmtp recibiría únicamente la salida del último echo. Para evitar este problema empaquetamos todos los comandos anteriores entre paréntesis:

 ORIGEN="$(hostname)@miDominio.org"
 DESTINO="alarmas@miDominio.org"
 (
    echo "Subject: Se detect'o un problema .... "
    echo "to: Alarmas MiDominio.org"
    echo ""
    echo "Errores Detectados:"
    echo "${ERRORES}"
    echo ""
    echo "/var/run/estado:"
    cat  /var/run/estado
    echo ""
    echo "e-mail generado autom'aticamente por ${0}@$(hostname)"
    echo ""
 ) | /usr/sbin/ssmtp -f ${ORIGEN} -F "Host $(hostname)" ${DESTINO}

Como se vé sólo hace falta utilizar la tubería | una sóla vez indicando que la salida de ( comando1 ... comandoN) sea desviada a /usr/sbin7ssmtp.

Parseando parámetros de un script "con estilo" editar

xwopts=
swfopts=
display="$DISPLAY"
while [ $# -gt 1 ]; do
    case "$1" in
        -name) shift; xwopts="$xwopts -name $1";;
        -id) shift; xwopts="$xwopts -id $1";;
        -display|-d) shift; display="$1"; xwopts="$xwopts -display $1";;
        -*) usage;;
    esac
    shift
done

Rotando logs (forma simple) editar

Aviso: GNU rottlog y/o logrotate (man 8 logrotate) son herramientas mucho más convenientes para rotar logs. El script mostrado a continuación sirve sólo como referencia.

/opt/crontasks/rotandoLogs.sh
#!/bin/sh

MAC=$(/sbin/ifconfig | grep "HWaddr" | sed "s/^.*HWaddr //" | sed "s/\ *//g")
LOGEO="/var/log/$(basename ${0}).${MAC}.log"      # Logeamos a rotandoLogs.sh.'MAC'.log
exec  1>>${LOGEO} 2>&1                            # Desviamos la salida y error estándar hacia $LOGEO.

cd /var/log/

for i1 in $(seq 9 -1 1); do
    i2=$(( i1 + 1 ))
    mv "MiImportanteLog.${i1}" "MiImportanteLog.${i2}" ; 
    done
done

mv "MiImportanteLog" "MiImportanteLog.0" ; 
touch "MiImportanteLog"
chmod a+rw "MiImportanteLog"

Para buscar un fichero y ejecutar un comando sobre el fichero encontrado editar

$ find ./ -name "*java,v" \
   -exec grep --with-filename tabla1 {} \;

La salida podría ser similar a:

./dir1/dir2/BeanLogCall.java,v: String query = "SELECT * FROM tabla1 WHERE id=?;";

find permite anidar condiciones OR (unión) mediante "-or". Por ejemplo para buscar nombres acabados en .c o .h:

$ find ./ -type f -name "*.c" -or -name "*.h"
(Si no indicamos nada añade la interxección -and )

Mini código para tratar locks de forma "segura" mediante find editar

Consideramos que un lock es válido si tiene 20 minutos o menos de antigüedad. En cualquier otro caso se considera un error (bien porque hay otro script todavía en ejecución, en cuyo caso abortamos, o porque el último script murió sin borrar el fichero). Todo el truco consiste en utilizar find con la opción "-mmin -20". En el siguiente script CONTINUAR/while se utilizan para poder saltar al final del bloque principal mediante break al igual que si tubiesemos una etiqueta :END e hiciesemos GOTO END. La variable ERRORES se utiliza se va rellenando. Al final del scripts si no está vacia se considerá que hubo un error y se logea y envía por correo.

#!/bin/sh
exec  1>>/var/log/CUSTOMSCRIPT_$(basename ${0}).$(whoami).log 2>&1  # Redireccionamos STDIO/ERR

echo "$(date +%Y-%m-%d\ %H:\%M) : Iniciando $(basename ${0})"
LOCKFILE="$(basename ${0}).$(whoami).lock"
RMLOCKONEXIT="1"

ERRORES=""
                                        CONTINUAR=1
                                        while [ ${CONTINUAR} != 0 ] ; do   # INIT WHILE *****
                                        CONTINUAR=0 # Por defecto terminamos en la primera interacci'on.
# = = = = = = = = = = = = = = = = = = = = 
# = = = =  COMPROBACIONES PREVIAS = = = = 
# = = = = = = = = = = = = = = = = = = = = 
find /var/run -maxdepth 1 -name ${LOCKFILE} -mmin -20 | grep "." 
if [ $? == 0 ] ; then
    ERRORES="${ERRORES}
-SE DETECTO /var/run/${LOCKFILE} creado antes de 20 minutos. " ERRORES="${ERRORES}
Abortamos la operaci'on" RMLOCKONEXIT="0" break; else find /var/run -maxdepth 1 -name ${LOCKFILE} | grep "." if [ $? == 0 ] ; then ERRORES="${ERRORES}
-SE DETECTO /var/run/${LOCKFILE} con m'as d 20 minutos de antiguedad" ERRORES="${ERRORES}
Continuamos y borramos el LOCK existente" find /var/run -maxdepth 1 -name ${LOCKFILE} -exec rm {} \; fi fi touch /var/run/${LOCKFILE} ... done # END WHILE ***** if [ "${RMLOCKONEXIT}" == "1" ]; then rm -f /var/run/${LOCKFILE} ; fi if [ ! "z${ERRORES}" == "z" ]; then ERRORES=$(echo "${ERRORES}" | sed "s/
/\n/g" | sed "s/^/ /") echo "Errores Detectados:" echo "${ERRORES}" ORIGEN="origen@mydomain.com" DESTINO="destino@mydomain.com" ( echo "Subject: Se detect'o un problema al procesar el fichero ${INPUTFILE}" echo "to: Administrador ..." echo "" echo "Errores Detectados:" # echo "${ERRORES}" | sed "s/
/\n/g" | sed "s/^/ /" echo "${ERRORES}" echo "" echo "e-mail generado autom'aticamente por ${0}@$(hostname)" echo "" ) | /usr/sbin/ssmtp -f ${ORIGEN} -F "$(hostname)" ${DESTINO} fi echo "$(date +%Y-%m-%d\ %H:\%M) : Finalizando $(basename ${0})"

Backup mysqldump de una bb.dd. editar

$> mysqldump -uABC -p???? nombreBaseDatos 
$> mysqldump -uABC -p???? nombreBaseDatos Tabla

Uso de socat para test de aplicaciones de red editar

P.ej, para simular el tráfico UDP hacia una aplicación escuchando en el puerto 30001:

$> socat UDP4-LISTEN:30001 -

En una segunda terminal lanzamos los datos simulados en un fichero de texto previamente creado:

$> socat OPEN:test.txt UDP4:192.168.0.101:30001


Captura de tráfico con tcpdump editar

P.ej, para capturar el tráfico udp que nos envían a un servidor ejecutamos como root en el propio servidor el comando:

root:~ # tcpdump -p -i eth2 -n -l udp -x -s 1500 dst port 22201 | tee /tmp/tcpdump.txt
Notas:
  1. tee permite copiar la salida estandar a uno o más ficheros al tiempo que permite ver la salida por pantalla.
  2. Si tenemos varios interfaces elegimos el que convenga mediante -i "nombre de interfaz".
  3. -p evita poner la interfaz en modo "promiscuo".
  4. -n evita mostrar nombres, solo número.
  5. -s 1500 muestra un máximo de 1500 bytes.
  6. dst port 22201 indica mostrar solo aquello destinado al puerto 33001 (udp restringe todavía más).

También se puede capturar a un archivo en modo binario, y luego recuperarlo:

root@~ # /usr/sbin/tcpdump  -i eth2 -l udp -s 126 -w /tmp/borrame2 dst port 22201

Y para verlo después:

root: # /usr/sbin/tcpdump -r /tmp/borrame2 -A

con -r lo recuperamos del archivo donde se guardó en el comando anterior, y con -A se ve en ASCII. También puede utilizarse Wireshark para leer el fichero capturado.

Configuración de acceso ssh sin contraseña editar

La idea es crear un conjunto de llave pública y privada, de forma que la llave pública la colocamos en el host remoto al que queremos acceder y la llave privada la guardamos localmente en el host local desde el que queremos conectar. Localmente creamos el par llave pública/privada mediante ssh-keygen:

usuario: $ ssh-keygen -t dsa -b 1024
Generating public/private dsa key pair.
Enter file in which to save the key (/home/miUsuario/.ssh/id_dsa): 
Enter passphrase (empty for no passphrase):
Enter same passphrase again:                *Nota 1
Your identification has been saved in /home/miUsuario/.ssh/id_dsa
...
Notas:
  1 El passphrase es un password normal y corriente que se utiliza para proteger la  llave privada en caso de robo.
  (para distinguir el password normal del password que protege la llave privada se habla de passphrase).
  En general es mejor dejar el passphrase en blanco si no necesitamos una seguridad paranoica 
  ya que complica la generaci'on de scripts y evita automatizar tareas.

A continuación (como root) copiamos el id_dsa a todos los usuarios que convenga (/home/miUsuario2/.ssh/id_dsa, /home/miUsuario3/.ssh/id_dsa,...) y en el servidor añadimos la clave pública (/home/"miUsuario/.ssh/id_dsa.pub) a la lista de llaves autorizadas de cada usuario que queramos (notar que el usuario1 en local puede querer acceder como un usuario diferente en la máquina remota). La forma más sencilla (suponiendo que ya hemos "subido" id_dsa.pub al servidor mediante un un correo, por samba, scp, memoryStick,...) es hacer algo similar a:

miUsuarioRemoto@servidor1 ~ cat id_dsa.pub >> /home/miUsuarioRemoto/.ssh/authorized_keys2
miUsuarioRemoto@servidor2 ~ cat id_dsa.pub >> /home/miUsuarioRemoto2/.ssh/authorized_keys2
(utilizamos ">>" -añadir- y no ">" para no eliminar otras clave autorizadas que ya pudiesen existir)
...

Backups diarios en red con ayuda de ssh editar

Una vez configuradio ssh sin contraseña es muy sencillo utilizar ssh para hacer copias en red utilizando el sistema estandar de tuberías Unix. El siguiente crontab muestra como hacerlo en práctica para copiar la base de datos de Zope/Plone y el repositorio CVS:

# m h  dom mon dow   command
# Copia Zope DataBase.
2 9 * * *  ping -q -c 1 -w 1  servidor1 && ssh -o "PasswordAuthentication=no" -o "KbdInteractiveAuthentication=no" -o "ChallengeResponseAuthentication=no" servidor1 "tar -cjf - /var/lib/zope2.7 /instance1/var/Data*" | cat - >>/BACKUPS/Data.tar.bz2@servidor1
# Copia de CVS
0 14 * * * ping -q -c 1 -w 1  servidor1 && ssh -o "PasswordAuthentication=no" -o "KbdInteractiveAuthentication=no" -o "ChallengeResponseAuthentication=no" servidor1 "tar cjf - /var/lib/cvs/" | cat - >> /BACKUPS/cvs.tar.bz2@servidor1
...

Al tratarse de un comando de red el prefijo "ping -q -c 1 -w 1 servidor1 &&" evita continuar con el comando si el ping ha fallado. ver man ping para una explicación detallada de las opciones (-q = "quiet" evita mandar texto a la salida estandar, -c 1 = realizar un solo ping, -w 1=esperar máximo 1 segundo). Algunas opciones útiles que podríamos pasar a ssh para scripts automatizados son las siguientes:

-o "CheckHostIP=no"             <-- Evita conflictos si los cambios de IP's equipos son habituales (menor seguridad)
-o "StrictHostKeyChecking=no"   <-- Evita conflictos si hemos reinstalado SSH,... (menor seguridad)
-o "PasswordAuthentication=no"         | <-- Los 3 comandos evitan que el script se quede colgado esperando el password
                                       |     si la llave  pública/privada dan error.
-o "KbdInteractiveAuthentication=no"   |
-o "ChallengeResponseAuthentication=no"|

'En general es una mala costumbre poner en el cron el comando ping ... && ssh. Es menos arriesgado y más práctico crear un script /opt/scriptsCRON/backup1.sh y añadir el mismo a la linea de cron, sin olvidar hacer backups del directorio /opt/scriptsCRON/'.

Ejemplo de copia en paralelo de varias máquinas editar

El siguiente script surgió de la necesidad de copiar los ficheros que se iban generando continuamente en varias máquinas (windows con cygwin+ssh) a un servidor apache bajo Linux:

 1 #!/bin/sh
 2 
 3 # ...
 4 # ...
 5 # ...
 6 
 7 REMOTE_SRC_DIR=/cygdrive/c/FicherosGenerados/
 8 LOCAL_DEST_DIR=/var/www/audios/
 9 
10 cd ${LOCAL_DEST_DIR} || exit 1;
11 
12 # Añadimos prefijo /tmp/crontab.root.nombreScript para evitar problemas a los
13 # fiche. temporales.
14 prefixTmp="/tmp/crontab.$(whoami).$(basename $0)."
15 for ip in 107 108 109 110 111 113
16 do
17         (
18         IP=192.168.2.${ip}
19         ping -q -c 1 -i 1 ${IP} 1>/dev/null
20         if [ $? -ne 0 ] ; then
21                 echo "fall'o ping a ${IP}"
22                 continue
23         fi
24         listadoFN=${prefixTmp}${wavsAMover}${IP}
25         ssh root@${IP} "cd ${REMOTE_SRC_DIR} && /bin/ls *wav" | /bin/cat > ${listadoFN}
26         cat ${listadoFN} | ssh root@${IP} "cd ${REMOTE_SRC_DIR} && /bin/tar --files-from=- -cf -" | tar xf -
27         if [ $? -ne 0 ] ; then
28                 echo "fall'o copia desde ${IP}"
29                 continue
30         fi
31         cat ${listadoFN} | ssh root@${IP} "cd ${REMOTE_SRC_DIR} && /bin/cat - | while read f ; do /bin/rm \${f} ;     done"
32         rm ${listadoFN}
33         echo "terminada copia ${IP}"
34         ) &
35 done

Notas:
# La línea 14 se encarga de crear prefijos "seguros" para ficheros temporales.
# Encerramos el cuerpo del bucle for ip in ... en ( ) & para que se ejecute 
  en background de forma que conseguimos "paralelizar" las copias (¡qué simple!)
# En la línea 25 listamos los ficheros que hay en ese momento de forma que 
  haremos un tar y rm basado en este fichero.  Si durante la ejecución del
  script se han creado nuevos ficheros, son ignorados. La "transacción"
  se limita al listado en el momento de ejecutar esta línea.
# En la línea 27 nos aseguramos que está todo OK o en caso contrario
  "saltamos" un tunel en sí ya que no reencamina todo el tráfico simulando 
  una conexión física, sino sólamente puertos TCP. 


Reencaminando puertos mediante SSH editar

Suponemos que tenemos una red local 192.168.1.X en la oficina 1 y una red local 192.168.2.X en la oficina 2. Suponemos que una máquina cliente Cliente1 de la oficina 1 dispone de acceso a ssh a un servidor (sshd) ServidorSSHOficina2 en la oficina 2 (basta con una sóla máquina en la oficina 1 y un sólo servidor en la oficina 2).

Para reencaminar el tráfico de un puerto/s en el cliente Cliente1 hacia un servidor/es y puerto en la red de la oficina 2 hacemos lo siguiente:

~@Cliente1> ssh -g -L MiPuertoLocalA:ServidorRemotoA:PuertoRemotoA  \
    -L  MiPuertoLocalB:ServidorRemotoB:PuertoRemotoB -L ... ServidorSSHOficina2
Notas:
   Utilizamos "\" para continuar el comando en la siguiente línea.
   ("\" debir ir seguido de retorno de carro. Si se añade un blanco dará error de sintaxis).
   -L indica que abrimos un puerto Local (en el cliente ssh) "a la escucha".
   -g abre el puerto "MiPuertoLocalA" en la dirección 0.0.0.0 en vez de en 127.0.0.1 
      permitiendo a otros puestos en la oficina 1 conectar a Cliente1.

Con esto conseguimos que al conectar a Cliente1:MiPuertoLocalA dentro de la oficina 1 veamos el servicio ServidorRemotoA:PuertoRemotoA de la oficina 2. ServidorRemotoA puede ser o no ser el mismo ServidorSSHOficina2. La única condición es que ServidorSSHOficina2 tenga acceso al mismo.

Igualmente podemos a la inversa hacer lo siguiente:

~@Cliente1> ssh -g -R puertoServidorLocalA:ServidorLocalA:PuertoRemotoA \
 -R puertoServidorLocalB:ServidorLocalB:PuertoRemotoB -R ... ServidorSSHOficina2

Notas:
 -R indica que abrimos un puerto Remoto (en el servidor ssh) "a la escucha".
 -g abre el puerto "PuertoRemotoA" en la dirección 0.0.0.0 en vez de en 127.0.0.1 
  permitiendo a otros puestos en la oficina 2 conectar a ServidorSSHOficina2.

Con esto conseguimos que al conectar a ServidorSSHOficina2:PuertoRemotoA dentro de la oficina 2 veamos el servicio ServidorLocalA:PuertoServidorLocalA de la oficina 1. ServidorLocalA puede ser o no ser el propio Cliente1. La única condición es que Cliente1 tenga acceso por red al mismo.

Tuneles con SSH (conexión punto a punto o "puente" ethernet) editar

AVISO: ¡¡¡los tuneles ssh utilizan apliamiento tcp sobre tcp y en general es peor opción que otras soluciones más avanzadas como openvpn, sin embargo es mucho más rápido de configurar!!!. Se supone que tanto el cliente como el servidor tienen configurado correctamente el soporte para "TUN" (tun permite crear interfaces "virtuales" de red dinámicamente).

En el servidor necesitamos editar /etc/ssh/sshd_config y añadir/modificar una línea con la opción

"PermitTunnel yes"

reiniciando el servicio para que los cambios tengan efecto:

/etc/init.d/ssh restart

A continuación conectamos con las siguientes opciones:

~@Cliente1> ssh -w 0:0 -o Tunnel=point-to-point ServidoSSHOficina2 
~@ServidorRemotoOficina2> su -c "ifconfig tun0 10.0.0.1 netmask 255.255.255.0 up"

En otra terminal local:

~@Cliente1> su -c "ifconfig tun0 10.0.0.2 netmask 255.255.255.0 up"

Comprobamos que los 2 extremos del punto a punto se ven mutuamente. En Cliente1 hacemos ping a 10.0.0.1 y en ServidorRemotoOficina2 hacemos ping a 10.0.0.2. A continuación bastará con crear las reglas de ruteo que consideremos necesarias. - Si en vez optar por una opción punto a punto utilizamos el modo "Bridge" (-o Tunnel=ethernet) el cliente y servidor ssh engañaran al protocolo ARP haciendo creen que los hosts de la oficina1 y 2 están unídos físicamente, pero puede generar conflictos de IP repetidas en ambos extremos. Básicamente en modo bridge (ethernet) cuando un host pregunta cual es la MAC física de una dirección, p.ej, 192.168.2.15, el cliente SSH "preguntará" al servidor en la oficina 2 si él conoce la IP y si el servidor le responde afirmativamente el cliente SSH responderá al host local "yo soy 192.168.2.15". A continuación cada vez que el host (que ha cacheado en su tabla ARP al Cliente SSH como la IP 192.168.2.15") haga una petición a dicha IP, el tráfico se reencaminará por el tunel SSH.

En general como usuario "no root", sin permisos especiales.

ping -c 1 -w 1 "direccionIP" && \
    mount -t smbfs -o uid=miusuario,gid=$migrupo,\
    iocharset=utf8,codepage=cp850,username=???,password=???,workgroup=???[,ro] \
    //"direccionIP"/"carpetaCompartida" /mnt/"puntoDeMontaje"/
Notas:
  # uid/gid son los usuarios locales de nuestra máquina (/etc/password, /etc/group) a quien queremos que pertnezca.
  (si no se indica nada root/root).
  # ping -q -c 1 -w 1 "direccionIP" &&  evita ejecutar el comando si tenemos problemas de red.
  # Para asegurar el valor del parámetro codepage abrimos una ventana "MSDOS" en Windows 
   y ejecutamos:
    $> chcp

Muy importante: CIFS/SMB/Samba no es fiable (2009-11-27). Su uso generalizado se debe simplemente a que es la forma estándar de conectar PCs Windows. Una opción mucho más fiable y resistente es instalar cygwin en las máquinas con Windows y utilizar ssh (scp, sftp,...) en su lugar. SSH además posibilita opciones imposibles con CIFS/SMB/Samba como p.ej, cifrado, sincronizar de volúmenes enormes de datos en segundos con ayuda de rsync, aplicar filtros antes de copiar o distribuir la carga de cpu entre dos sistemas en red.

Una vez comprobado que funciona manualmente podemos añadir a /etc/fstab para que se monte automáticamente añadiendo una línea similar a:

 //"direccionIP"/"carpetaCompartida" /mnt/"puntoDeMontaje"  smbfs uid=miusuario,gid=migrupo,...  0 0

Como referencia y para comparar. Si utilizamos NFS en lugar de Samba (NFS es mucho más rápido, estable y fiable pero sólo en redes Linux/UNIX, no windows):

 mount -t nfs //"miDireccionIP"/"miCarpetaCompartida"   /mnt/"puntoDeMontaje:
 Notas:
   # Antes de hacer nada con nfs hay que activar el demonio portmap si es que estaba desactivado

En /etc/fstab:

 //"direccionIP"/"carpetaCompartida"  /mnt/"puntoDeMontaje"  nfs   defaults 0 0

Generar un fichero core cuando se produzca un fallo de segmentación editar

En bash:

ulimit -c unlimited

En tcsh:

limit coredumpsize unlimited

Al ejecutar un programa que genere un "SEGMENTATION FAULT" el kernel creará un fichero de nombre "core". Entonces podremos utilizar este core como entrada al depurador gdb para saber exáctamente dónde se generó el fallo. Ej:

~ ulimit -c unlimited
~ ./miProgramaDePrueba
...
SEGMENTATION FAULT ('core' generated)
~ gdb ./miProgramaDePrueba core
Leyendo símbolos desde /lib/libglib-2.0.so.0...(no debugging symbols found)...hecho.
...
El núcleo se generó por «./miProgramaDePrueba».
El programa terminó con la señal 6, Aborted.

(gdb) bt
#0  0x005f6422 in __kernel_vsyscall ()
#1  0x00b2f4d1 in raise () from /lib/tls/i686/cmov/libc.so.6
#2  0x00b32932 in abort () from /lib/tls/i686/cmov/libc.so.6
#3  0x00b65ee5 in ?? () from /lib/tls/i686/cmov/libc.so.6
#4  0x00b6fff1 in ?? () from /lib/tls/i686/cmov/libc.so.6
#5  0x00b716f2 in ?? () from /lib/tls/i686/cmov/libc.so.6
#6  0x00b747cd in free () from /lib/tls/i686/cmov/libc.so.6
#7  0x0080f196 in g_free () from /lib/libglib-2.0.so.0
#8  0x00f59c54 in ctrans_free_resources (pTrans=0x9039008) at miBiblioteca.c:87
#9  0x00f59b1e in ctrans_new_transaction (stack_ptr2Top=0xbfc4cd1c, ppTrans=0xbfc4cd2c, 
    pParentTrans=0x0, sDebug=0x80488f7 "trans1") at miBiblioteca.c:53
#10 0x0804876d in run_main_loop () at miProgramaDePrueba.c:67
#11 0x0804872c in main (nargs=1, args=0xbfc4cdf4) at miProgramaDePrueba.c:59

bt (back trace) muestra las últimas funciones invocadas antes de producirse el error. En este caso puede verse que la línea miBiblioteca.c llamó a la función estándar g_free de libglib en la línea 87, probablemente con un argumento no válido (puntero nulo, ...).

Disminuir la velocidad del ratón en X editar

Muy útil para programas de dibujo/autocad/...

xset m 1/2 1