El Virtual Network Device de VMWare Player 4.0.2 no compila en Ubuntu Precise 12.04 (Linux Kernel 3.2)

May 2nd, 2012 6 comments

La instalación de VMWare Player 4.0.2 en Ubuntu Precise 12.04 parece que funciona sin problemas, pero en el primer inicio de la aplicación nos pide compilar una serie de módulos y falla al compilar el Virtual Network Device.

Aparentemente VMWare Player 4.0.2 (y VMWare Workstation 8.0.2) no está preparado para compilar su driver de red virtual contra el kernel 3.2 de Linux (aunque en el 3.3 sí funciona). Afortunadamente Stefano Angeleri ha publicado un fix que soluciona el problema. Se trata de un parche para el módulo de red virtual de VMWare. Para aplicarlo:

  1. Descargamos el parche de aquí y extraemos el contenido.
  2. Vamos a /usr/lib/vmware/modules/source y descomprimimos vmnet.tar.
  3. Aplicamos el parche que extrajimos antes.
  4. Comprimimos de nuevo la carpeta con las fuentes parcheadas.
  5. Recompilamos los módulos de vmware.

Los comandos a ejecutar:

  1. wget http://weltall.heliohost.org/wordpress/wp-content/uploads/2012/01/vmware802fixlinux320.tar.gz
  2. mkdir vmware-patch
  3. mv vmware802fixlinux320.tar.gz vmware-patch/
  4. cd vmware-patch
  5. tar xvzf vmware802fixlinux320.tar.gz
  6. cd /usr/lib/vmware/modules/source
  7. sudo tar xvf vmnet.tar
  8. sudo patch -p1 < /home/chema92/Downloads/vmware-patch/vmware3.2.0.patch
  9. sudo tar cvf vmnet.tar vmnet-only/
  10. sudo vmware-modconfig –console –install-all

En el terminal queda así:

chema92@chema92-office:~/Downloads$ wget http://weltall.heliohost.org/wordpress/wp-content/uploads/2012/01/vmware802fixlinux320.tar.gz
--2012-05-02 12:00:33--  http://weltall.heliohost.org/wordpress/wp-content/uploads/2012/01/vmware802fixlinux320.tar.gz
Resolving weltall.heliohost.org (weltall.heliohost.org)... 216.218.192.170
Connecting to weltall.heliohost.org (weltall.heliohost.org)|216.218.192.170|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2033 (2.0K) [application/x-gzip]
Saving to: `vmware802fixlinux320.tar.gz'
100%[==================================================================================================>] 2,033       --.-K/s   in 0s
2012-05-02 12:00:40 (3.97 MB/s) - `vmware802fixlinux320.tar.gz' saved [2033/2033]
chema92@chema92-office:~/Downloads$ mkdir vmware-patch
chema92@chema92-office:~/Downloads$ mv vmware802fixlinux320.tar.gz vmware-patch/
chema92@chema92-office:~/Downloads$ cd vmware-patch/
chema92@chema92-office:~/Downloads/vmware-patch$ ls
vmware802fixlinux320.tar.gz
chema92@chema92-office:~/Downloads/vmware-patch$ tar xvzf vmware802fixlinux320.tar.gz
patch-modules_3.2.0.sh
vmware3.2.0.patch
chema92@chema92-office:~/Downloads/vmware-patch$ cd /usr/lib/vmware/modules/source
chema92@chema92-office:/usr/lib/vmware/modules/source$ ls
vmblock.tar  vmci.tar  vmmon.tar  vmnet.tar  vsock.tar
chema92@chema92-office:/usr/lib/vmware/modules/source$ sudo tar xvf vmnet.tar
vmnet-only/
vmnet-only/vm_device_version.h
vmnet-only/Makefile.normal
vmnet-only/vnetEvent.h
vmnet-only/vm_oui.h
vmnet-only/filter.c
vmnet-only/vm_basic_asm.h
vmnet-only/COPYING
vmnet-only/vm_assert.h
vmnet-only/nfhook_uses_skb.c
vmnet-only/netdev_has_net.c
vmnet-only/vnetUserListener.c
vmnet-only/vmware_pack_init.h
vmnet-only/vnetEvent.c
vmnet-only/procfs.c
vmnet-only/compat_skbuff.h
vmnet-only/Makefile.kernel
vmnet-only/vnetFilter.h
vmnet-only/vnet.h
vmnet-only/vmnetInt.h
vmnet-only/vm_basic_asm_x86.h
vmnet-only/compat_sock.h
vmnet-only/x86cpuid.h
vmnet-only/Makefile
vmnet-only/smac_compat.c
vmnet-only/netif.c
vmnet-only/vmware_pack_end.h
vmnet-only/bridge.c
vmnet-only/hub.c
vmnet-only/smac.h
vmnet-only/vnetInt.h
vmnet-only/compat_autoconf.h
vmnet-only/includeCheck.h
vmnet-only/vmware_pack_begin.h
vmnet-only/vnetKernel.h
vmnet-only/vm_basic_defs.h
vmnet-only/community_source.h
vmnet-only/vnetFilterInt.h
vmnet-only/smac.c
vmnet-only/vm_basic_types.h
vmnet-only/netdev_has_dev_net.c
vmnet-only/driver-config.h
vmnet-only/userif.c
vmnet-only/monitorAction_exported.h
vmnet-only/compat_module.h
vmnet-only/compat_netdevice.h
vmnet-only/vm_basic_asm_x86_64.h
vmnet-only/smac_compat.h
vmnet-only/geninclude.c
vmnet-only/driver.c
vmnet-only/skblin.c
vmnet-only/compat_version.h
vmnet-only/net.h
vmnet-only/vm_atomic.h
chema92@chema92-office:/usr/lib/vmware/modules/source$ ls
vmblock.tar  vmci.tar  vmmon.tar  vmnet-only  vmnet.tar  vsock.tar
chema92@chema92-office:/usr/lib/vmware/modules/source$ sudo patch -p1 < /home/chema92/Downloads/vmware-patch/vmware3.2.0.patch
patching file vmnet-only/filter.c
patching file vmnet-only/netif.c
patching file vmnet-only/userif.c
chema92@chema92-office:/usr/lib/vmware/modules/source$ sudo tar cvf vmnet.tar vmnet-only/
vmnet-only/
vmnet-only/vm_device_version.h
vmnet-only/Makefile.normal
vmnet-only/vnetEvent.h
vmnet-only/vm_oui.h
vmnet-only/filter.c
vmnet-only/vm_basic_asm.h
vmnet-only/COPYING
vmnet-only/vm_assert.h
vmnet-only/nfhook_uses_skb.c
vmnet-only/netdev_has_net.c
vmnet-only/vnetUserListener.c
vmnet-only/vmware_pack_init.h
vmnet-only/vnetEvent.c
vmnet-only/procfs.c
vmnet-only/compat_skbuff.h
vmnet-only/Makefile.kernel
vmnet-only/vnetFilter.h
vmnet-only/vnet.h
vmnet-only/vmnetInt.h
vmnet-only/vm_basic_asm_x86.h
vmnet-only/compat_sock.h
vmnet-only/x86cpuid.h
vmnet-only/Makefile
vmnet-only/smac_compat.c
vmnet-only/netif.c
vmnet-only/vmware_pack_end.h
vmnet-only/bridge.c
vmnet-only/hub.c
vmnet-only/smac.h
vmnet-only/vnetInt.h
vmnet-only/compat_autoconf.h
vmnet-only/includeCheck.h
vmnet-only/vmware_pack_begin.h
vmnet-only/vnetKernel.h
vmnet-only/vm_basic_defs.h
vmnet-only/community_source.h
vmnet-only/vnetFilterInt.h
vmnet-only/smac.c
vmnet-only/vm_basic_types.h
vmnet-only/netdev_has_dev_net.c
vmnet-only/driver-config.h
vmnet-only/userif.c
vmnet-only/monitorAction_exported.h
vmnet-only/compat_module.h
vmnet-only/compat_netdevice.h
vmnet-only/vm_basic_asm_x86_64.h
vmnet-only/smac_compat.h
vmnet-only/geninclude.c
vmnet-only/driver.c
vmnet-only/skblin.c
vmnet-only/compat_version.h
vmnet-only/net.h
vmnet-only/vm_atomic.h
chema92@chema92-office:/usr/lib/vmware/modules/source$ ls
vmblock.tar  vmci.tar  vmmon.tar  vmnet-only  vmnet.tar  vsock.tar
chema92@chema92-office:/usr/lib/vmware/modules/source$ sudo vmware-modconfig --console --install-all
Stopping VMware services:
   VMware Authentication Daemon                                        done
   VM communication interface socket family                            done
   Virtual machine communication interface                             done
   Virtual machine monitor                                             done
   Blocking file system                                                done
Using 2.6.x kernel build system.
make: Entering directory `/tmp/vmware-root/modules/vmmon-only'
make -C /lib/modules/3.2.0-24-generic/build/include/.. SUBDIRS=$PWD SRCROOT=$PWD/. \
	  MODULEBUILDDIR= modules
make[1]: Entering directory `/usr/src/linux-headers-3.2.0-24-generic'
  CC [M]  /tmp/vmware-root/modules/vmmon-only/linux/driver.o
  CC [M]  /tmp/vmware-root/modules/vmmon-only/linux/driverLog.o
  CC [M]  /tmp/vmware-root/modules/vmmon-only/linux/hostif.o
  CC [M]  /tmp/vmware-root/modules/vmmon-only/common/apic.o
  CC [M]  /tmp/vmware-root/modules/vmmon-only/common/comport.o
  CC [M]  /tmp/vmware-root/modules/vmmon-only/common/cpuid.o
  CC [M]  /tmp/vmware-root/modules/vmmon-only/common/hashFunc.o
  CC [M]  /tmp/vmware-root/modules/vmmon-only/common/memtrack.o
  CC [M]  /tmp/vmware-root/modules/vmmon-only/common/phystrack.o
  CC [M]  /tmp/vmware-root/modules/vmmon-only/common/task.o
  CC [M]  /tmp/vmware-root/modules/vmmon-only/common/vmx86.o
  CC [M]  /tmp/vmware-root/modules/vmmon-only/vmcore/moduleloop.o
  LD [M]  /tmp/vmware-root/modules/vmmon-only/vmmon.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /tmp/vmware-root/modules/vmmon-only/vmmon.mod.o
  LD [M]  /tmp/vmware-root/modules/vmmon-only/vmmon.ko
make[1]: Leaving directory `/usr/src/linux-headers-3.2.0-24-generic'
make -C $PWD SRCROOT=$PWD/. \
	  MODULEBUILDDIR= postbuild
make[1]: Entering directory `/tmp/vmware-root/modules/vmmon-only'
make[1]: `postbuild' is up to date.
make[1]: Leaving directory `/tmp/vmware-root/modules/vmmon-only'
cp -f vmmon.ko ./../vmmon.o
make: Leaving directory `/tmp/vmware-root/modules/vmmon-only'
Built vmmon module
Using 2.6.x kernel build system.
make: Entering directory `/tmp/vmware-root/modules/vmnet-only'
make -C /lib/modules/3.2.0-24-generic/build/include/.. SUBDIRS=$PWD SRCROOT=$PWD/. \
	  MODULEBUILDDIR= modules
make[1]: Entering directory `/usr/src/linux-headers-3.2.0-24-generic'
  CC [M]  /tmp/vmware-root/modules/vmnet-only/driver.o
  CC [M]  /tmp/vmware-root/modules/vmnet-only/hub.o
  CC [M]  /tmp/vmware-root/modules/vmnet-only/netif.o
  CC [M]  /tmp/vmware-root/modules/vmnet-only/userif.o
  CC [M]  /tmp/vmware-root/modules/vmnet-only/bridge.o
  CC [M]  /tmp/vmware-root/modules/vmnet-only/filter.o
  CC [M]  /tmp/vmware-root/modules/vmnet-only/procfs.o
  CC [M]  /tmp/vmware-root/modules/vmnet-only/smac_compat.o
  CC [M]  /tmp/vmware-root/modules/vmnet-only/smac.o
  CC [M]  /tmp/vmware-root/modules/vmnet-only/vnetEvent.o
  CC [M]  /tmp/vmware-root/modules/vmnet-only/vnetUserListener.o
  LD [M]  /tmp/vmware-root/modules/vmnet-only/vmnet.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /tmp/vmware-root/modules/vmnet-only/vmnet.mod.o
  LD [M]  /tmp/vmware-root/modules/vmnet-only/vmnet.ko
make[1]: Leaving directory `/usr/src/linux-headers-3.2.0-24-generic'
make -C $PWD SRCROOT=$PWD/. \
	  MODULEBUILDDIR= postbuild
make[1]: Entering directory `/tmp/vmware-root/modules/vmnet-only'
make[1]: `postbuild' is up to date.
make[1]: Leaving directory `/tmp/vmware-root/modules/vmnet-only'
cp -f vmnet.ko ./../vmnet.o
make: Leaving directory `/tmp/vmware-root/modules/vmnet-only'
Built vmnet module
Using 2.6.x kernel build system.
make: Entering directory `/tmp/vmware-root/modules/vmblock-only'
make -C /lib/modules/3.2.0-24-generic/build/include/.. SUBDIRS=$PWD SRCROOT=$PWD/. \
	  MODULEBUILDDIR= modules
make[1]: Entering directory `/usr/src/linux-headers-3.2.0-24-generic'
  CC [M]  /tmp/vmware-root/modules/vmblock-only/linux/block.o
  CC [M]  /tmp/vmware-root/modules/vmblock-only/linux/control.o
  CC [M]  /tmp/vmware-root/modules/vmblock-only/linux/dbllnklst.o
  CC [M]  /tmp/vmware-root/modules/vmblock-only/linux/dentry.o
  CC [M]  /tmp/vmware-root/modules/vmblock-only/linux/file.o
  CC [M]  /tmp/vmware-root/modules/vmblock-only/linux/filesystem.o
  CC [M]  /tmp/vmware-root/modules/vmblock-only/linux/inode.o
  CC [M]  /tmp/vmware-root/modules/vmblock-only/linux/module.o
  CC [M]  /tmp/vmware-root/modules/vmblock-only/linux/stubs.o
  CC [M]  /tmp/vmware-root/modules/vmblock-only/linux/super.o
  LD [M]  /tmp/vmware-root/modules/vmblock-only/vmblock.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /tmp/vmware-root/modules/vmblock-only/vmblock.mod.o
  LD [M]  /tmp/vmware-root/modules/vmblock-only/vmblock.ko
make[1]: Leaving directory `/usr/src/linux-headers-3.2.0-24-generic'
make -C $PWD SRCROOT=$PWD/. \
	  MODULEBUILDDIR= postbuild
make[1]: Entering directory `/tmp/vmware-root/modules/vmblock-only'
make[1]: `postbuild' is up to date.
make[1]: Leaving directory `/tmp/vmware-root/modules/vmblock-only'
cp -f vmblock.ko ./../vmblock.o
make: Leaving directory `/tmp/vmware-root/modules/vmblock-only'
Built vmblock module
Using 2.6.x kernel build system.
make: Entering directory `/tmp/vmware-root/modules/vmci-only'
make -C /lib/modules/3.2.0-24-generic/build/include/.. SUBDIRS=$PWD SRCROOT=$PWD/. \
	  MODULEBUILDDIR= modules
make[1]: Entering directory `/usr/src/linux-headers-3.2.0-24-generic'
  CC [M]  /tmp/vmware-root/modules/vmci-only/linux/driver.o
  CC [M]  /tmp/vmware-root/modules/vmci-only/linux/vmciKernelIf.o
  CC [M]  /tmp/vmware-root/modules/vmci-only/common/vmciContext.o
  CC [M]  /tmp/vmware-root/modules/vmci-only/common/vmciDatagram.o
  CC [M]  /tmp/vmware-root/modules/vmci-only/common/vmciDoorbell.o
  CC [M]  /tmp/vmware-root/modules/vmci-only/common/vmciEvent.o
  CC [M]  /tmp/vmware-root/modules/vmci-only/common/vmciDriver.o
  CC [M]  /tmp/vmware-root/modules/vmci-only/common/vmciHashtable.o
  CC [M]  /tmp/vmware-root/modules/vmci-only/common/vmciQPair.o
  CC [M]  /tmp/vmware-root/modules/vmci-only/common/vmciQueuePair.o
  CC [M]  /tmp/vmware-root/modules/vmci-only/common/vmciResource.o
  CC [M]  /tmp/vmware-root/modules/vmci-only/common/vmciRoute.o
  CC [M]  /tmp/vmware-root/modules/vmci-only/driverLog.o
  LD [M]  /tmp/vmware-root/modules/vmci-only/vmci.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /tmp/vmware-root/modules/vmci-only/vmci.mod.o
  LD [M]  /tmp/vmware-root/modules/vmci-only/vmci.ko
make[1]: Leaving directory `/usr/src/linux-headers-3.2.0-24-generic'
make -C $PWD SRCROOT=$PWD/. \
	  MODULEBUILDDIR= postbuild
make[1]: Entering directory `/tmp/vmware-root/modules/vmci-only'
make[1]: `postbuild' is up to date.
make[1]: Leaving directory `/tmp/vmware-root/modules/vmci-only'
cp -f vmci.ko ./../vmci.o
make: Leaving directory `/tmp/vmware-root/modules/vmci-only'
Built vmci module
Using 2.6.x kernel build system.
make: Entering directory `/tmp/vmware-root/modules/vsock-only'
make -C /lib/modules/3.2.0-24-generic/build/include/.. SUBDIRS=$PWD SRCROOT=$PWD/. \
	  MODULEBUILDDIR= modules
make[1]: Entering directory `/usr/src/linux-headers-3.2.0-24-generic'
  CC [M]  /tmp/vmware-root/modules/vsock-only/linux/af_vsock.o
  CC [M]  /tmp/vmware-root/modules/vsock-only/linux/notify.o
  CC [M]  /tmp/vmware-root/modules/vsock-only/linux/notifyQState.o
  CC [M]  /tmp/vmware-root/modules/vsock-only/linux/stats.o
  CC [M]  /tmp/vmware-root/modules/vsock-only/linux/util.o
  CC [M]  /tmp/vmware-root/modules/vsock-only/linux/vsockAddr.o
  CC [M]  /tmp/vmware-root/modules/vsock-only/driverLog.o
  LD [M]  /tmp/vmware-root/modules/vsock-only/vsock.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /tmp/vmware-root/modules/vsock-only/vsock.mod.o
  LD [M]  /tmp/vmware-root/modules/vsock-only/vsock.ko
make[1]: Leaving directory `/usr/src/linux-headers-3.2.0-24-generic'
make -C $PWD SRCROOT=$PWD/. \
	  MODULEBUILDDIR= postbuild
make[1]: Entering directory `/tmp/vmware-root/modules/vsock-only'
make[1]: `postbuild' is up to date.
make[1]: Leaving directory `/tmp/vmware-root/modules/vsock-only'
cp -f vsock.ko ./../vsock.o
make: Leaving directory `/tmp/vmware-root/modules/vsock-only'
Built vsock module
Starting VMware services:
   Virtual machine monitor                                             done
   Virtual machine communication interface                             done
   VM communication interface socket family                            done
   Blocking file system                                                done
   Virtual ethernet                                                    done
   VMware Authentication Daemon                                        done
   Shared Memory Available                                             done

Configurar node.js como servicio en CentOS 5.7 con monit

March 29th, 2012 No comments

Una de las críticas más habituales de node.js es que muchas veces uno termina corriendo sus aplicaciones como root dentro de screen, lo que no es precisamente un ejemplo de buenas prácticas. Recientemente hemos puesto (casi) en produccion una nueva aplicación corriendo sobre node, así que hemos tenido que buscar una solución un poco más profesional, con un script de inicio que se pueda monitorizar para que siempre esté corriendo.

En el servidor tenemos CentOS 5.7. Si tuviesemos ya la versión 6 el proceso sería diferente porque CentOS ha abandonado System V y ahora usa upstart para arrancar los servicios, como ya venías haciendo Ubuntu y otras distros.

Además del script de inicio hemos instalado monit, una servicio que monitoriza otros servicios y es capaz, por ejemplo, de reiniciarlos si comprueba que no se cumplen ciertas condiciones pre-establecidas por nosotros.

Lo primero es añadir un usuario para que corra node. Como nosotros usamos node para un servidor con Express en el puerto 8001, no hacen falta privilegios especiales:

[jose@devel ~]$ sudo groupadd -r node
[jose@devel ~]$ sudo useradd -r --shell /bin/bash -g node  --home /var/lib/node node

Le podíamos haber puesto la home en /home/node también, pero al ser un servicio he preferido dejarlo en /var/lib al estilo mysql.

Respecto al script de inicio, hemos partido de este script de Yusuke Narita. El problema era que una vez lanzado, no nos genera un pidfile en /var/run, y esto nos hace falta para el script de monit. Para solucionarlo hemos añadido este código al principio del script original:

# Creamos un fichero PID para monit
SCRIPT="$(basename $0)"
PIDFILE="/var/run/$SCRIPT.pid"
ps -fe | grep "$SCRIPT" | head -n1 | cut -d" " -f 6 > ${PIDFILE}

También tenemos que ocuparnos de borrar el pidfile una vez que paramos el servicio, así que en la zona correspondiente al stop hemos añadido:

if [ -f ${PIDFILE} ]; then
        rm ${PIDFILE}
        fi

Y por último, hemos cambiado también la forma de obtener el pid del servicio que utilizaba el propio script. En vez de:

pid=`ps -aefw | grep "$DAEMON $SERVER" | grep -v " grep " | awk '{print $2}'`

Hemos usado el mismo método que cuando creamos el pidfile:

PID=`ps -fe | grep "$SCRIPT" | head -n1 | cut -d" " -f 6`

De esta forma usamos el nombre de nuestro servicio ($0 en bash) en vez de las rutas de node y nuestra aplicación web.

El script de inicio queda así:

#!/bin/sh
#
# chkconfig: 35 99 99
# description: Node.js /home/nodejs/sample/app.js
#
. /etc/rc.d/init.d/functions
# Creamos un fichero PID para monit
SCRIPT="$(basename $0)"
PIDFILE="/var/run/$SCRIPT.pid"
ps -fe | grep "$SCRIPT" | head -n1 | cut -d" " -f 6 > ${PIDFILE}
USER="node"
DAEMON="/usr/local/bin/node"
ROOT_DIR="/var/www/vhosts/mydomain.com/mywebapp"
SERVER="$ROOT_DIR/app.js"
LOG_FILE="$ROOT_DIR/app.js.log"
LOCK_FILE="/var/lock/subsys/node-server"
do_start()
{
        if [ ! -f "$LOCK_FILE" ] ; then
                echo -n $"Starting $SERVER: "
                runuser -l "$USER" -c "$DAEMON $SERVER >> $LOG_FILE &" && echo_success || echo_failure
                RETVAL=$?
                echo
                [ $RETVAL -eq 0 ] && touch $LOCK_FILE
        else
                echo "$SERVER is locked."
                RETVAL=1
        fi
}
do_stop()
{
        echo -n $"Stopping $SERVER: "
       # pid=`ps -aefw | grep "$DAEMON $SERVER" | grep -v " grep " | awk '{print $2}'`
        PID=`ps -fe | grep "$SCRIPT" | head -n1 | cut -d" " -f 6`
        kill -9 $PID > /dev/null 2>&1 && echo_success || echo_failure
        if [ -f ${PIDFILE} ]; then
        rm ${PIDFILE}
        fi
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE
}
case "$1" in
        start)
                do_start
                ;;
        stop)
                do_stop
                ;;
        restart)
                do_stop
                do_start
                ;;
        *)
                echo "Usage: $0 {start|stop|restart}"
                RETVAL=1
esac
exit $RETVAL

Por último nos queda añadir la configuración de nuestro nuevo servicio en monit. Ojo que la instalación por defecto de monit en CentOS (yum install monit) nos crea un fichero con su propia configuración en /etc/monit.conf, pero monit espera que se llame /etc/monitrc. Para arreglarlo le creamos un enlace simbólico y listos:

[jose@devel backend]$ sudo ln -s /etc/monit.conf /etc/monitrc

Creamos un fichero myapp o como queramos (no tiene por qué llamarse igual que el servicio) en /etc/monit.d:

set logfile /var/log/monit
check process myapp with pidfile "/var/run/myapp.pid"
        start program = "/etc/init.d/myapp start"
        stop program = "/etc/init.d/myapp stop"
        if failed port 8001 protocol HTTP
                request /
                with timeout 10 seconds
                then restart

Dónde básicamente le pedimos que controle el proceso myapp (nuestro servicio) y que haya un protocolo HTTP respondiendo en el puerto 8001. Si falla, que reinicie el servicio.

Cuidado que la primera vez que lanzamos monit nos apareció el siguiente error:

[jose@devel ~]$ sudo service monit start
Starting monit: monit: Error opening the idfile '/var/monit/id' -- No such file or directory

El problema es que monit espera que exista /var/monit, pero el spec del rpm no nos lo ha creado. Creamos el directorio y ya funcionará sin preoblemas.

[jose@devel ~]$ sudo mkdir /var/monit
[jose@devel ~]$ sudo service monit start
Starting monit: monit: generated unique Monit id 22d6190a446ca8eadfd0ba65ad46f759 and stored to '/var/monit/id'
Starting monit daemon with http interface at [localhost:2812]

Por último lo configuramos para inicio automático:

[jose@devel ~]$ sudo chkconfig monit on

El script está disponible para descarga aquí.

NuSOAP no puede abrir sockets en CentOS (SELinux)

March 24th, 2012 No comments

Al tratar de utilizar nuSOAP en una máquina recién instalada con Centos 5.4 (recién porque me hacía falta simular un entorno de producción con PHP 5.1.6), aparece el siguiente error:

wsdl error: HTTP ERROR: Couldn't open socket connection to server http://soap.mydomain.com/clientservice/clientservice.asmx?WSDL, Error (13): Permission denied

Sabemos que este mismo código funciona en producción, así que debe ser algo relacionado con nuestra máquina en particular. Paramos apache y lo volvemos a lanzar con strace, a ver qué está pasando exactamente:

[jose@localhost ~]$ sudo /etc/init.d/httpd stop
Stopping httpd:                                            [  OK  ]
[jose@localhost ~]$ sudo strace -f -o trace /etc/init.d/httpd start
Starting httpd:                                            [  OK  ]

Esto nos dejará en el fichero trace todas las llamadas del sistema operativo que haya hecho Apache mientras ha estado corriendo. Así que recargamos la página que lanza el error y ya tendremos la información que nos interesa. Ojo que para salir de strace tendremos que parar Apache (salvo que le hayamos puesto un contador de tiempo).

En la traza nos encontramos:

[jose@localhost ~]$ grep 'Permission denied' trace.txt
6828  access("/etc/sysconfig/i18n", X_OK) = -1 EACCES (Permission denied)
6828  access("/etc/sysconfig/init", X_OK) = -1 EACCES (Permission denied)
6828  access("/etc/sysconfig/httpd", X_OK) = -1 EACCES (Permission denied)
6833  open("/etc/selinux/config", O_RDONLY|O_LARGEFILE) = -1 EACCES (Permission denied)
6833  open("/selinux/mls", O_RDONLY|O_LARGEFILE) = -1 EACCES (Permission denied)
6837  connect(19, {sa_family=AF_INET, sin_port=htons(8118), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EACCES (Permission denied)

En la última línea vemos la conexión al puerto 8118 de privoxy en la máquina local que nos ha sido denegada. Como estamos en una distro de la familia Redhat, el sospechoso número uno del bloqueo es SELinux.
Comprobamos /var/log/audit/audit.log y nos encontramos los siguiente:

type=SYSCALL msg=audit(1332615354.434:144): arch=40000003 syscall=102 success=no exit=-13 a0=3 a1=bff20770 a2=1e5b874 a3=92a7474 items=0 ppid=6709 pid=6713 auid=0 uid=48 gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=6 comm="httpd" exe="/usr/sbin/httpd" subj=root:system_r:httpd_t:s0 key=(null)
type=AVC msg=audit(1332615363.526:145): avc:  denied  { name_connect } for  pid=6715 comm="httpd" dest=8118 scontext=root:system_r:httpd_t:s0 tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket

Vemos el intento de Apache de abrirnos el socket en el puerto 8118 (donde tenemos corriendo nuestro proxy) y como ha sido denegado por SELinux, con error 13 (exit=-13).

Nos vamos a la man page correspondiente (man httpd_selinux):

httpd scripts by default are not allowed to connect out to the network.
This would prevent a hacker from breaking into you httpd server and attacking other machines. If you need scripts to be able to connect you can set the httpd_can_network_connect boolean on.
setsebool -P httpd_can_network_connect 1

Y ya vemos que tenemos que cambiar un booleano en la configuración de SELinux:

[jose@localhost ~]$ sudo setsebool -P httpd_can_network_connect 1
Categories: Apache, CentOS, Linux Tags: , , ,

Bug en NuSOAP ignora el puerto del proxy HTTP

March 23rd, 2012 No comments

Me he estado volviendo loco tratando de averiguar por qué NuSOAP estaba ignorando el proxy http (privoxy haciendo forward al proxy socks de ssh) y tiraba errores 404. Al final encontré la solución gracias a Marcel Berteler. Por lo visto en la clase soap_transport_http, la función connect ignora la configuración y utiliza el puerto del WSDL.

El código original:

if($connection_timeout > 0){
  $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout);
} else {
  $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str);
}

Y la modificación:

if($connection_timeout > 0){
  $this->fp = @fsockopen( $host, $port, $this->errno, $this->error_str, $connection_timeout);
} else {
  $this->fp = @fsockopen( $host, $port, $this->errno, $this->error_str);
}

Como vemos sólo ha cambiado el segundo parámetro de la llamada a fsockopen, que de $this->port pasa a $port.

Categories: PHP Tags: , , , ,

Deshacer un merge en Subversion

March 23rd, 2012 No comments

Acabamos de hacer un merge y nos hemos dado cuenta que no era necesario, antes de hacer el commit. Para arreglarlo usamos:

[jose@devel]$ svn revert -R .

Usamos -R para hacer el revert recursivamente.

Categories: Subversion Tags: , , ,

Instalar VirtualBox Guest Additions en BT5 R2

February 29th, 2012 2 comments

Al tratar de instalar las Guest Additions de VirtualBox en BT5 R2 el instalador nos dice que no encuentra las cabeceras  del kernel:

root@bt:/media/VBOXADDITIONS_4.1.8_75467# ./VBoxLinuxAdditions.run
The headers for the current running kernel were not found. If the following module compilation fails then this could be the reason.
Building the main Guest Additions module ...fail!
(Look at /var/log/vboxadd-install.log to find out what went wrong)

Y si miramos el log:

Error! Your kernel headers for kernel 3.2.6 cannot be found at
/lib/modules/3.2.6/build or /lib/modules/3.2.6/source.
You can use the --kernelsourcedir option to tell DKMS where it's located, or you could intall the linux-headers-3.2.6 package.
Failed to install using DKMS, attemting to install without
/tmp/vbox.0/Makefile.include.header:94: *** Error: unable to find the sources of your current Linux kernel. Specify KERN_DIR= and run Make again. Stop.

Encontré la solución en Pebbler (¡gracias!):

root@bt~#:prepare-kernel-sources

Luego nos vamos a /usr/src/linux y ejecutamos:

root@bt:/usr/src/linux# cp -rf include/generated/* include/linux/

Y por último exportamos la ruta correcta:

root@bt:/usr/src/linux# KERN_DIR=/usr/src/linux
root@bt:/usr/src/linux# export KERN_DIR

Y listo, ya podemos lanzar el instalador de Guest Additions desde el menú Devices - Install Guest Additions. O también desde consola, por ejemplo con:

root@bt:/media/VBOXADDITIONS_4.1.8_75467/VBoxLinuxAdditions.run

Al tratar de instalar las Guest Additions de VirtualBox en BT5 R2 el instalador nos dice que no encuentra las cabeceras  del kernel:

root@bt:/media/VBOXADDITIONS_4.1.8_75467# ./VBoxLinuxAdditions.run
The headers for the current running kernel were not found. If the following module compilation fails then this could be the reason.
Building the main Guest Additions module ...fail!
(Look at /var/log/vboxadd-install.log to find out what went wrong)

Y si miramos el log:

Error! Your kernel headers for kernel 3.2.6 cannot be found at
/lib/modules/3.2.6/build or /lib/modules/3.2.6/source.
You can use the --kernelsourcedir option to tell DKMS where it's located, or you could intall the linux-headers-3.2.6 package.
Failed to install using DKMS, attemting to install without
/tmp/vbox.0/Makefile.include.header:94: *** Error: unable to find the sources of your current Linux kernel. Specify KERN_DIR= and run Make again. Stop.

Encontré la solución en Pebbler (¡gracias!):

root@bt~#:prepare-kernel-sources

Luego nos vamos a /usr/src/linux y ejecutamos:

root@bt:/usr/src/linux# cp -rf include/generated/* include/linux/

Y por último exportamos la ruta correcta:

root@bt:/usr/src/linux# KERN_DIR=/usr/src/linux
root@bt:/usr/src/linux# export KERN_DIR

Y listo, ya podemos lanzar el instalador de Guest Additions desde el menú Devices - Install Guest Additions. O también desde consola, por ejemplo con:

root@bt:/media/VBOXADDITIONS_4.1.8_75467/VBoxLinuxAdditions.run
Categories: Linux Tags: , ,

Notificación de cambios en el seguimiento de envíos de correos.es

December 2nd, 2011 No comments

La web de correos.es no nos ofrece (o yo no he sabido encontrarlo) la posibilidad de notificarnos por e-mail de los cambios en el estado de un envío.

Como esta semana estaba esperando un envío importante de Alemania, he hecho un pequeño script en Perl que me avise de todas las actualizaciones que vayan ocurriendo. Basta con ponerlo en un cron (yo lo he puesto cada media hora) y nos enviará un e-mail cuando detecte un cambio.

Como pre-requisitos tenemos los siguientes modulos:

  • LWP::Simple – Para traernos la página con el seguimiento del envío.
  • HTML::TreeBuilder::XPath – Para extraer la información que nos interesa de la web.
  • Net::SMTP – Para enviar el e-mail de aviso,

Ojo que el script está hecho en 30 minutos y habría mucho por refactorizar (asi de entrada, lo de tener las direcciones de e-mail hardcoded es de juzgado de guardia). En cualquier caso, es un buen ejemplo de como Perl (y CPAN), pueden hacernos la vida más fácil. El código es el siguiente y podéis usarlo para lo que queráis:

#!/usr/bin/perl -w
use strict;
use warnings;
use utf8;
use LWP::Simple;
use HTML::TreeBuilder::XPath;
use Net::SMTP;
@ARGV == 1 or die "uso: track.pl NUMERO_TRACKING\n";
my $number = $ARGV[0];
my $content = get("http://www.correos.es/comun/Localizador/track.asp?numero=$number");
die "Error al traer la pagina" unless defined $content;
my $page = new HTML::TreeBuilder::XPath->new();
$page->parse($content);
$page->eof();
# Buscamos la tabla con id=Table2
# Es la que tiene los resultados del tracking
my @rows = $page->findvalues('//table[@id="Table2"]/tr');
my $num_rows = @rows-2;					# nº de estados del envio en la web
# Salida de datos por pantalla
# La guardamos para utilizarla despues en el e-mail
my $output = "";
$output .= "Numero: $number\t\tEstados: $num_rows\n";
for (my $row=2;$row;
    close $file;
    if ($num_rows>$line) {				# Si hubo cambios (estados web > estados fichero)
        open my $file, '>', $counter_path or die $!;	# Actualizamos el fichero con el nuevo numero
        print $file $num_rows;
        close $file;
        print "\nEstado del paquete actualizado, enviando email...\n";
	# Enviamos e-mail de aviso
	# En nuestro caso con SMTP autenticado
        my $smtp = Net::SMTP->new('mailserver.com', Debug => 1, Timeout => 30, Hello => 'mailserver.com') or die "Error creando objeto SMTP: $!\n";
        $smtp->auth("miusuariodecorreo","micontraseñadecorreo");
	# Dialogo SMTP con el server
	$smtp->mail('remitente@mail.com');
	$smtp->to('destinatario@mail.com');
	$smtp->cc('otrodestinatario@mail.com');
	# Cabeceras y cuerpo del mensaje
	# Ojo que al ser comillas dobles hay que escapar las arrobas para no interpolar sin querer
	$smtp->data();
	$smtp->datasend("From: remitente\@mail.com\n");
	$smtp->datasend("To: destinatario\@mail.com\n");
	$smtp->datasend("Cc: otrodestinatario\@mail.com\n");
	$smtp->datasend("Subject: Cambio de estado del envio $number\n");
	$smtp->datasend("\n");
	$smtp->datasend($output."\n");
	$smtp->dataend();
	$smtp->quit;
    }
} else {						# Si el fichero de datos no existe, lo creamos
    open my $file, '>', $counter_path or die $!;	# y guardamos el primer estado de la web
    print $file $num_rows;
    close $file;
}

Y por último nos quedaría meterlo en el crontab, por ejemplo cada media hora con */30 * * * *.

NOTA: Para que funcione el SMTP autenticado hará falta tener instalado Authen::SASL::Perl.

Categories: Perl Tags: ,

Borrar directorios recursivamente

October 18th, 2011 No comments

Con este comando podemos borrar recursivamente todos los directorios cuyo nombre coincida con el patrón indicado después de -name. Por ejemplo, para borrar todos los directorios .svn de una working copy que ya no necesitamos versionada:

find . -name .svn -print0 | xargs -0 rm -rf
Categories: Bash, Subversion Tags: ,

Ruta correcta para file_get_contents tanto en local como en producción

October 18th, 2011 No comments

Nos gustaría que file_get_contents encuentre su destino correctamente tanto en el servidor de desarrollo como en producción. Para conseguirlo tenemos que generar la ruta en varios pasos:

  1. El esquema, en nuestro caso http
  2. El servidor, que lo obtenemos de $_SERVER.
  3. La ruta, suponiendo que queremos la misma desde nuestro script actual, pero sin el nombre del script en sí.

Y así es como quedaría:

file_get_contents('http://' . $_SERVER['HTTP_HOST'] . dirname($_SERVER['SCRIPT_NAME']) . '/foo.php?bar=1');
Categories: PHP Tags: ,

For-loop de bash en una sola línea

April 26th, 2011 No comments

Por ejemplo, para borrar todos los archivos de un .tar.gz que acabamos de descomprimir por accidente:

jose@office:~$ for i in $(tar tzf miarchivo.tar.gz); do rm -rf $i; done

¿Y qué ocurre cuando nuestro fichero comprimido contiene nombres con espacios? El bucle for usa como separador de elementos el espacio en blanco (entre otros caracteres contenidos en la variable de entorno IFS), así que interpretará cada palabra del nombre de fichero como un fichero distinto. Para evitarlo revisamos nuestro oneliner de la siguiente forma:

OLDIFS=$IFS; IFS=$(echo -en "\n\b"); for i in $(tar tzf queso.tar.gz); do echo "$i"; done; IFS=$OLDIFS

Lo primero que hemos hecho es guardar el IFS (Internal Field Separator) en una variable. Luego lo redefinimos para que deje de ser el espacio en blanco (dejamos como separador los códigos de escape de nueva línea \n y de backspace \b).

Categories: Bash Tags: , ,