"Después del juego es antes del juego"
Sepp Herberger

martes, 31 de mayo de 2016

2x1: multiseat en las aulas (Parte I)

Tengo algunos equipos con tanta potencia y memoria de sobra que da vergüenza ponerlos como PC para un único usuario. Podría utilizarlos como hasta ahora, con servidores LTSP o bien servidores x2go, pero eso ya está hecho y había que rizar el rizo.

Mi compañero del IES Bembézar me había hablado de multiseat, gracias al cual podemos convertir 1 PC en dos (o cuatro, o n) puestos de usuario conectando 2 monitores, 2 teclados y 2 ratones = 2 "seat". 2x1. ¿Sería fácil de montar con Xubuntu 14.04?, pues me puse manos a la obra para comprobarlo.

Me he guiado principalmente por esta página de Ubuntu, aunque la mayoría de información viene de posts de foros y preguntas en stackoverflow y sitios similares....

1. Hardware que necesitamos.

Nos hacen falta 1 PC, 2 teclados, 2 ratones y 2 monitores. Para esta implementación, que es la mas sencilla y rápida, también necesitamos que en el PC haya 2 tarjetas VGA, una para cada "seat".

Si queremos que haya sonido independiente en cada seat debemos tener también 2 tarjetas de sonido. Por supuesto los periféricos deben ser compatibles con los conectores que tenga nuestro PC: no podemos poner 2 teclados PS2 si solo hay una salida PS2, claro está. Ídem para los monitores: usaremos cables y conectores VGA, DVI, HDMI, etc en función de lo que haya.

2. Preparando el entorno.

Bueno, pues para preparar esto tenemos que empezar añadiendo un repositorio ppa:
# sudo add-apt-repository ppa:ubuntu-multiseat/ppa
Que nos añadirá:
# cat /etc/apt/sources.listd.d/ubuntu-multiseat-ppa-trusty.list 
deb http://ppa.launchpad.net/ubuntu-multiseat/ppa/ubuntu trusty main
# deb-src http://ppa.launchpad.net/ubuntu-multiseat/ppa/ubuntu trusty main
Y actualizamos los paquetes, especialmente los relaciones con las X para soporte multiseat :
# sudo apt-get update
# sudo apt-get upgrade
Tambien hay que editar
# cat /etc/lightdm/lightdm.conf
[LightDM]
logind-load-seats=true
Para que lightdm reconozca la configuración de los seat y abra una sesión en cada monitor, con todo el hardware asociado.

Tampoco viene mal quitar el paquete light-locker. Hemos observado que produce pantallas en negro y bloqueos especialmente en el seat secundario. Si tenemos pkgsync lo mejor es ponerlo en maynothave.

3. Comandos básicos.

Antiguamente la configuración era mas pedestre, editando a mano fichero en /etc/udev/rules.d para asignar el hardware a los distintos dispositivos. Ahora es mas sencillo, primero veremos cómo listar los seat configurados:
 # loginctl list-seats
SEAT            
seat0           

1 seats listed.
Como se ve, por defecto solo hay un seat, el seat0. Todos los dispositivos están asociados a él, vamos a listarlos. El comando para listar los dispositivos asociados a un seat determinado es (los colores son aportación mía para identificar los componentes mejor, ojo: esto es mi máquina, en la tuya saldrán otras cosas):
 # loginctl seat-status seat0
seat0
Sessions: *c2 c1
 Devices:
   ├─/sys/devices/LNXSYSTM:00/LNXPWRBN:00/input/input1
   │ input:input1 "Power Button"
   ├─/sys/device...XSYSTM:00/LNXSYBUS:00/PNP0C0C:00/input/input0
   │ input:input0 "Power Button"
   ├─/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/drm/card0
   │ drm:card0
   ├─/sys/device...0:00/0000:00:01.0/0000:01:00.0/drm/renderD128
   │ drm:renderD128
   ├─/sys/device...000:00/0000:00:01.0/0000:01:00.0/graphics/fb0
   │ [MASTER] graphics:fb0 "radeondrmfb"
   ├─/sys/device...0000:00/0000:00:01.0/0000:01:00.1/sound/card1
   │ sound:card1 "HDMI"
   │ └─/sys/device...000:00:01.0/0000:01:00.1/sound/card1/input7
   │   input:input7 "HDA ATI HDMI HDMI/DP,pcm=3"
   ├─/sys/devices/pci0000:00/0000:00:1a.0/usb3
   │ usb:usb3
   ├─/sys/devices/pci0000:00/0000:00:1a.1/usb4
   │ usb:usb4
   ├─/sys/devices/pci0000:00/0000:00:1a.2/usb5
   │ usb:usb5
   ├─/sys/devices/pci0000:00/0000:00:1a.7/usb1
   │ usb:usb1
   │ └─/sys/devices/pci0000:00/0000:00:1a.7/usb1/1-3
   │   usb:1-3
   ├─/sys/devices/pci0000:00/0000:00:1b.0/sound/card0
   │ sound:card0 "Intel"
   │ ├─/sys/devices/pci0000:00/0000:00:1b.0/sound/card0/input10
   │ │ input:input10 "HDA Intel Line"
   │ ├─/sys/devices/pci0000:00/0000:00:1b.0/sound/card0/input11
   │ │ input:input11 "HDA Intel Line Out Front"
   │ ├─/sys/devices/pci0000:00/0000:00:1b.0/sound/card0/input12
   │ │ input:input12 "HDA Intel Line Out Surround"
   │ ├─/sys/devices/pci0000:00/0000:00:1b.0/sound/card0/input13
   │ │ input:input13 "HDA Intel Line Out CLFE"
   │ ├─/sys/devices/pci0000:00/0000:00:1b.0/soundosdosd/card0/input14
   │ │ input:input14 "HDA Intel Line Out Side"
   │ ├─/sys/devices/pci0000:00/0000:00:1b.0/sound/card0/input15
   │ │ input:input15 "HDA Intel Front Headphone"
   │ ├─/sys/devices/pci0000:00/0000:00:1b.0/sound/card0/input8
   │ │ input:input8 "HDA Intel Front Mic"
   │ └─/sys/devices/pci0000:00/0000:00:1b.0/sound/card0/input9
   │   input:input9 "HDA Intel Rear Mic"
   ├─/sys/devices/pci0000:00/0000:00:1c.0/0000:05:00.0/drm/card1
   │ drm:card1
   ├─/sys/device...0:00/0000:00:1c.0/0000:05:00.0/drm/renderD129
   │ drm:renderD129
   ├─/sys/device...000:00/0000:00:1c.0/0000:05:00.0/graphics/fb1
   │ [MASTER] graphics:fb1 "nouveaufb"
   ├─/sys/devices/pci0000:00/0000:00:1d.0/usb6
   │ usb:usb6
   │ ├─/sys/device...-1/6-1:1.0/0003:0458:003A.0001/input/input3
   │ │ input:input3 "Genius Optical Mouse"
   │ ├─/sys/device...-2/6-2:1.0/0003:046D:C316.0003/input/input5
   │ │ input:input5 "Logitech Logitech USB Keyboard"
   │ └─/sys/device...-2/6-2:1.1/0003:046D:C316.0004/input/input6
   │   input:input6 "Logitech Lde configuraciónogitech USB Keyboard"
   ├─/sys/devices/pci0000:00/0000:00:1d.1/usb7
   │ usb:usb7
   ├─/sys/devices/pci0000:00/0000:00:1d.2/usb8
   │ usb:usb8
   │ └─/sys/device...-1/8-1:1.0/0003:0458:003A.0002/input/input4
   │   input:input4 "Genius Optical Mouse"
   ├─/sys/devices/pci0000:00/0000:00:1d.7/usb2
   │ usb:usb2
   ├─/sys/device...1f.2/ata2/host1/target1:0:0/1:0:0:0/block/sr0
   │ block:sr0
   ├─/sys/device...a2/host1/target1:0:0/1:0:0:0/scsi_generic/sg1
   │ scsi_generic:sg1
   ├─/sys/devices/platform/i8042/serio0/input/input2
   │ input:input2 "AT Translated Set 2 keyboard"
   └─/sys/devices/virtual/misc/kvm
     misc:kvm

Los colores (recordemos: puestos por mi a mano para que quede clarito) tienen el siguiente significado:
  • Rojo: ratones. 
  • Verde: teclados. 
  • Azul: hub usb externo.
  • Naranja: tarjetas VGA.
  • Morado: tarjetas sonido.
Bueno, pues ahi estan las tarjetas gráficas, teclados, ratones, botones de todo tipo (el de apagado, reset, etc), puertos usb y tarjetas de sonido asociados al seat.

Veamos el hardware con el que estoy trabajando en mi caso (nótese que el teclado PS2 no sale ya que ni lsusb ni lspci lo muestran):
# lsusb
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 008 Device 002: ID 0458:003a KYE Systems Corp. (Mouse Systems) NetScroll+ Mini Traveler / Genius NetScroll 120
Bus 008 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 007 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 006 Device 003: ID 046d:c316 Logitech, Inc. HID-Compliant Keyboard
Bus 006 Device 002: ID 0458:003a KYE Systems Corp. (Mouse Systems) NetScroll+ Mini Traveler / Genius NetScroll 120
Bus 006 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 003: ID 05e3:0608 Genesys Logic, Inc. Hub
Bus 001 Device 002: ID 05e3:0716 Genesys Logic, Inc. USB 2.0 Multislot Card Reader/Writer
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
# lspci | grep -i vga
01:00.0 VGA compatible controller: Advanced Micro Devices, Inc. [AMD/ATI] RV620 LE [Radeon HD 3450]
05:00.0 VGA compatible controller: NVIDIA Corporation G72 [GeForce 7300 LE] (rev a1)
# lspci | grep -i audiodos
00:1b.0 Audio device: Intel Corporation 82801JI (ICH10 Family) HD Audio Controller
01:00.1 Audio device: Advanced Micro Devices, Inc. [AMD/ATI] RV620 HDMI Audio [Radeon HD 3400 Series]
Como se puede apreciar, para mi PC yo tengo 2 teclados (1 USB y 1 PS2), 2 ratones (ambos USB), 2 tarjetas VGA y HUB externo USB conectados al puesto donde voy a configurar el multiseat.

En el caso de que no identifiquemos algun dispositivo en la aparatosa lista que muestra seat-status, podemos probar a desconectarlo y hacer un:
 # loginctl seat-status seat0 > antes.txt
Luego conectarlo y otra vez:
# loginctl seat-status seat0 > despues.txt
# diff antes.txt despues.txt
Y así en las diferencias entre ambos ficheros veremos la parte que describe dicho dispositivo.

Antes de seguir en el siguiente post debemos analizar tranquilamente la información que nos da nuestro hardware particular y tener claro qué es cada dispositivo y como vamos a repartirlo entre los ambos seat, dejando algunos en seat0 y moviendo otros a seat-1.

Seguimos en breve en otro post...


viernes, 27 de mayo de 2016

Printer Wars III: Oh, Brother!

"Oh, Brother!", divertida película de los hermanos Cohen y divertida peripecia que he tenido con el servicio técnico de esa marca de impresoras.

1) El problema.

El centro compró una impresora Brother DCP-9020-CDW hace mas de un año. Es una impresora láser color multifunción que cumplía nuestros requisitos:

  • Precio ajustado de impresora y repuestos de tóner compatible (las directivas de la UE no obligan, pero si recomiendan a las administraciones públicas usar tóner y tinta compatible. A fin de cuentas jugamos con el dinero de todos).
  • Características ajustadas a nuestras necesidades.
  • Buen soporte para Linux.
  • La confianza de la marca Brother, que viene haciendo impresoras desde tiempos inmemoriales.

Bueno, pues tras funcionar bien durante varios meses usando cartuchos de tóner compatibles de repente empezó a dejar una arruga con aspecto de "grabado" en el papel al imprimir. No era una mancha, era un "bajorelieve" que deformaba el papel con un patrón periódico cada 4 o 5 cm. Las hojas impresas quedaban poco presentables con dicho defecto.

Puestos al habla con el servicio técnico de Brother me dicen como acceder al fusor para ver si veo algo sospechoso, ya que la avería proviene de allí con toda seguridad, en la misma llamada y de forma sibilina me dan indicaciones para averiguar si tengo tóner compatible u original. Esto último podrían haberlo preguntado directamente, no tengo nada que ocultar.

Siguiendo sus instrucciones, en el fusor veo que uno de los rodillos rodeado por un film negro ( el "heat roller") tiene partes de dicho film quitadas y deformadas, siendo esa deformidad la que causa el "grabado" en el papel. Bien, es una avería y estamos en garantía, vamos a tramitarla. Empieza lo bueno:

  1. Me dicen que para que el taller recepcione la impresora debe ir con tóner original de Brother. En caso contrario por sistema se achaca la avería al tóner compatible.
  2. Que para darme instrucciones para la recogida debo mandar por fax una factura con la compra del tóner original adquirido ex-profeso para la ocasión. Si no la mando, no me dan ninguna información sobre como proceder.
  3. Un juego de tóneres originales cuesta casi tanto como una impresora nueva.
  4. No me pueden asegurar que la garantía cubra la avería, después de todo podría ser por una mala actuación de algún usuario
  5. Recuerdo que el tóner que venía con la impresora, el llamado "kit de inicio", lo guardamos al poco tiempo de llegar. Estaba al 80%. Lo pongo y contacto con ellos para decir que la envío con ese tóner original. Me responden que no les vale el "kit de inicio", debo comprar tóner nuevo o no hay tutía.
  6. Les planteo contactar con otro centro que tenga la misma impresora y, en el caso de tener juegos de tóner original, me los prestase para gestionar la garantía. No, debo comprar tóner nuevo y enviar la factura, no hay otra opción. Esta última llamada es bastante tensa ya que Magdalena, la persona que me atendió, me recuerda que ella hace su trabajo y yo la recuerdo que yo hago el mío, que entre otras muchas cosas es conseguir una Administración mas eficiente técnica y económicamente, ya que ese tóner que quieren obligarme comprar se va a pagar con sus impuestos y los míos. Esa afirmación debe ser bastante violenta para ella, ya que en ese punto se despide y me cuelga el teléfono.
  7. En resumen: Brother solo gestiona las garantías si pasas por caja previamente y compras tóner original (les da igual el establecimiento) y les envías la factura. Otro caso más en que una empresa no parece de vivir de fabricar impresoras, sino de vender los consumibles para las mismas. Adiós, Brother.

Bueno, he desmontado el fusor y esto se ve:


Mas cerca, el "heat roller" con el recubrimiento negro todo arrugado y deformado:


Es curioso, los rodillos de goma inferiores tienen pinta de estar erosionados:



Desde luego, ni eso ni lo otro tiene pinta de ser causado por un tóner no original.

2) Buscando mas damnificados.

Investigando estos días en Internet descubro que esto no es nuevo ni soy el primero: vean este enlace, donde aparte de describir mi problema con la misma impresora en distintas partes del mundo, dicen:

Well full marks to Brother. Customer services gave usual story of being out of guarantee and gave me the number of local service engineer but did say I should contact Brothers complaints dept. Emailed complaints who responded within the hour. I the sent them photo of the mark on the Fuser roller and the usage printout. Next reply was that they would carry out a free home inspection. The Service Company came out in a couple of days with a new unit. All now working OK and most important, no charge. Just hoping that this one will last and they have upgraded the part(wishful thinking maybe).
-------------------------
Hi! Brother service here in Sweden just replaced my fuser unit (guarantee, half a year, 1100 printed pages). It seems to be a well-known problem. Mechanical marks/tracks down the page, typically on the left-hand side of the paper. I asked if I will need to replace it twice a year in the future. He answered vaguely that software updates and general product development might fix it in the future. Luckily, I have 3 years guarantee...

Es decir, el problema es conocido por Brother y lo están arreglando de forma gratuita a esa gente de USA y Suecia. Incluso dicen:

If I visit the Brother DCP-9020CDW Support and Downloads Page (English, EU page), I get a pop-up tab on the bottom stating "For optimum performance of your printer, perform an update to the latest firmware. This may help to prevent paper wrinkle or smudge printing." This sounds like some of the issues people have been having in this thread. The firmware date and version is 23/12/2015, (T/1.07/M). Does this help anyone, or been there, done that, didn't work?

3) Investigando.

¿Será cierto eso?, pues parece que si:


Es decir, que recomiendan un nuevo firmware para prevenir el "paper wrinkle" y "smudge printing", con lo cual implícitamente reconocen que es un problema suyo. En Brother España dicen:


Nótese el Lost in translation (estupenda película): en la versión inglesa, el "prevent paper wrinkle" se traduce a "mejorar la alimentación del papel". Google Translate me lo traduce por "prevenir las arrugas de papel": carajo, justo lo que me pasaba a mi. Alguien está engañando a alguien.

Recalco que no es un aviso genérico: sólo sale con este modelo de impresora. Buscando un poco más encuentro otro comentario en Amazon:


Toma ya, les han reemplazado 3 fusores gratis, uno de ellos fuera de garantía. Ya no me hace falta buscar más.

4) La conclusión.

Meanwhile, aquí en Hispanistán me han confirmado por teléfono esta misma mañana otra vez que debo gastar 280 euros en tóner original para que ellos me solucionen (o no) un problema reconocido de sus impresoras defectuosas. Spain is different.

Bueno, pues mientras que decidimos que hacer (al ser una Admnistración Pública no podemos demandar como consumidores a Brother, deberíamos ir por la via judicial, cosa que tampoco merece la pena) solo puedo quedarme con dos moralejas:
  1. Nunca sabremos que pasó a la impresora, ya que Brother no llegará a examinarla, aunque siempre sospecharemos que es un fallo del firmware en el que Brother España elude su responsabilidad.
  2. Nunca más Brother, ni comprar ni recomendar su uso. No me parece ético comprar a empresas que recurren al chantaje y al abuso de poder para forzarte a que adquieras sus consumibles, mientras se niegan a arreglar un defecto reconocido por ellos mismos.

Oh, Brother:



Adenda 8-Enero-2018: me llega este post del foro https://www.printerforums.net:
Atomic Shrimp:

I've had this problem on two different examples of this printer (one at home, one at work) - in the most recent case - actually I suspect both cases - the damage seemed to happen after the printer was left waiting for paper.

Someone printed a job that required more paper than was in the tray - the printer sat there saying 'add paper' with a fan running for hours; the printer is in a different room, so nobody noticed - and I think the fuser unit was being kept hot all of this time, softening or melting the plastic on the roller.

I wonder if more recent firmware might manage the power for the fuser unit more intelligently in the case that a job is interrupted midway due to supplies running out. If not, the workaround for me is to make sure that any time you print anything, you attend the printer promptly to ensure it finished and that the printer has gone back to sleep.
Según parece ser, el firmware original tiene un problema: si la impresora se queda sin papel sin que nadie se de cuenta, el fusor se queda caliente de forma indefinida, fundiéndose el plástico del roller. El firmware mas reciente apaga el calentador del fusor cuando lleva un rato parado sin que entre mas papel en la alimentación. Es una pena que haya sido un tercero y no Brother quien reconozca este fallo. Estoy casi seguro de que eso me pasó a mi: quedó algo pendiente de imprimir una tarde o un fin de semana y así continuó hasta que se fundió el plástico.

lunes, 23 de mayo de 2016

Problemas diversos con las X y GRUB_CMDLINE _LINUX_DEFAULT en Ubuntu 14.04

Estos días he tenido varios problemas con un par de tarjetas VGA y el Ubuntu 14.04 que estamos instalando. Como me ha costado un rato de Google encontrar la solución pongo las recetas para tenerlas a mano.

En primer caso es una:
00:02.0 VGA compatible controller [0300]: Intel Corporation Sky Lake Integrated Graphics [8086:1912] (rev 06)
que se encuentra en los equipos que llamamos "infolab".

Inicialmente el driver "intel" funcionaba bien, pero tras varios días de uso me encontré con que aleatoriamente se cerraba sesión (sobre todo con Flash Player), unas ventanas se superponían con otras o bien se quedaban con rectángulos negros sin refrescar. Haciendo pruebas encontré que configurando aceleración "uxa" se solucionaba el problema:
 # cat /etc/X11/xorg.conf 
Section "ServerLayout"
.....
.....

Section "Device"
        .....
        .....
 Option "AccelMethod" "uxa"
 Identifier  "Card0"
 Driver      "intel"
 BusID       "PCI:0:2:0"
        .....
        .....


EndSection

.....
.....
EndSection
¿Solucionado del todo?. Pues no: al encender el PC el cursor del ratón se volvía "invisible" y podías moverlo y hacer click sin verlo. Si iniciaba sesión y salía de la misma (a ciegas), al cargar el nuevo el lightdm ya si se hacía visible. Desde luego eso no es operativo. Buscando y buscando encontré la solucion: editar /etc/default/grub quitar 'quiet splash' de GRUB_CMDLINE_LINUX_DEFAULT, quedando:
GRUB_CMDLINE_LINUX_DEFAULT=""
No hay que olvidar luego hacer:
# update-grub2
Realmente lo que hace esto es que la carga del sistema no muestre la pantalla bonita en modo gráfico y salga el tradicional listado de texto estilo "matrix". Es decir, que la configuración gráfica no se hace hasta la carga de las X. Y con eso solucioné el primer problema.

El segundo era con unas antiguas tarjetas VGA:
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation G72 [GeForce 7300 LE] [10de:01d1] (rev a1)
En este caso los drivers nouveau y nvidia quedaban la pantalla completamente negra. El nvidia además de regalo mantenía las xorg por encima del 90% de CPU. Probé varias versiones del driver propietario (nvidia-173, nvidia-304, descarga directa de nvidia.com) y de distintos ppa sin éxito.

El driver VESA, que se carga por defecto blacklistando los módulos nouveau y nvidia, no daba problemas pero su rendimiento es escaso y la reproducción de vídeo no era nada fluída. Después de buscar y buscar (y probar sin éxito la solución anterior) encontré una referencia a poner "nomodeset" en GRUB_CMDLINE_LINUX_DEFAULT y ¡voilá!... empezó a funcionar con nouveau. El resultado es tan fluído que ni he intentado probar con nvidia.

Realmente, lo que hace nomodeset es no intentar configurar la resolución y frecuencias de pantalla durante la carga del kernel, dejando eso para cuando se carguen las X.

Y ahí queda eso, al final ha sido mas fácil que intentar formar gobierno el 27 de junio.


domingo, 22 de mayo de 2016

Montemos una IP-Webcam barata - Reloaded I.

Esto es un sinfín. Reconozco que esta entrada y sus predecesoras han tenido mucho éxito gracias a la llegada de gente desde el estupendo foro de Openwrt de SeguridadWireless y, como tengo en producción el sistema desde que escribí el primer post, sigo con ello realizando retoques y mejoras con cierta frecuencia.

La último ha sido unos problemas derivados de la caída del servidor donde se almacenan las capturas. Como ya vimos es un servidor remoto desde el que montamos una carpeta por sshfs. He tenido diversos problemas con dicho servidor e inoportunas caídas del servicio. Los problemas detectados son:
  1. Cuando se interrumpe la conexión sshfs ya no se recupera hasta que se reinicia el router. Ese reinicio lo tengo programado una vez al día a medianoche.
  2. Si se interrumpe la conexión el guardado de las imágenes se hace en /mnt local, con lo cual los pocos megas de espacio de almacenamiento interno del router se llenan rápidamente y dejan el OpenWrt malfuncionando. Al reiniciar el router ese espacio se libera borrando las capturas.
  3. En general, las capturas hechas durante el tiempo que está el sshfs caído se pierden como lágrimas en la lluvia, ya que muchas veces no se llegan a enviar por problemas derivados del llenado del almacenamiento interno.
Para evitar estos problemas he implementado cambios orientados a detectar que no está montado el sshfs, intentar remontarlo y, si no se puede, adaptarnos a esta situación. En líneas generales:
  1. He ordenado un poco el código creando varias funciones, sacando al programita de los años 50 del siglo pasado.
  2. He creado un fichero local llamado /mnt/testigo que solo será visible si el sistema remoto no está montado por sshfs.
  3. Al principio pregunto si el sistema sshfs está montado y en caso negativo intento re-montarlo.
  4. Si no hay manera de remontarlo y no hay movimiento detectado, el fichero con la imagen *snapshot* no se almacena en la memoria interna: se borra.
  5. Si no hay manera de remontarlo y hay movimiento detectado, los ficheros con la secuencia son guardados en el espacio de almacenamiento interno de forma local y, cuando llegan a ser 10, empaquetados y enviados por correo, borrándose luego. El "10" es un número arbitrario que cada cual puede variar para ajustarlo a su espacio de almacenamiento
Y ya está, veamos el código:
# cat /root/controlador.sh 
#!/bin/ash

log() {
   test $montado -eq 1 && echo "$1" >> /mnt/snapshot/log.txt  
}

email() {
   test $email -eq 1 && echo -e "$1" | sendmail correo.aviso@gmail.com
}

monta_sshfs() {
  sshfs -o ssh_command="ssh -i /root/.ssh/id_rsa -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" -o nonempty openwrt@192.168.0.25:/home/openwrt /mnt
}

envia_secuencia() {

   if [ -e /tmp/motion.txt ]
   then

      #Recopila todos los ficheros de /tmp/motion/txt y los envia en un correo

      ficheros=$(cat /tmp/motion.txt | tr '\n' ' ')
      rm /tmp/motion.txt

      log "Historia de capturas $ficheros"
      zip -9 /mnt/escena.zip $ficheros

      echo "Secuencia $1" > /mnt/mensaje.txt
      test $email -eq 1 && mutt -s "Evento camara" correo.aviso@gmail.com -a /mnt/escena.zip < /mnt/mensaje.txt

      rm /mnt/escena.zip
      #Si no hay acceso al almacenamiento remoto, borramos las capturas para no llenar el disco del router.

      test $montado -eq 0 && rm -rf $destino

   fi

}

#Parametro 1: start, picture, end
#Parametro 2: fecha
#Parametro 3: nombre fichero (opcional)

#Obtenemos fecha y hora por si queremos poner algun filtro sobre ella y solo avisar en determinados
#momentos

time=$(date +%s)
anio=$(date +%Y @$time)
mes=$(date +%m @$time)
dia=$(date +%d @$time)
hora=$(date +%H @$time)
minuto=$(date +%M @$time)
diasemana=$(date +%u @$time) #El 1 es lunes
destino="/mnt/snapshot/$anio-$mes-$dia"
montado=1
email==0

if [ $diasemana -ge 6 -o $hora -le 7 -o $hora -ge 17 ]  # sabado/domingo o cualquier dia antes de las 8:00 o despues de las 17:00
then
   email=1
fi

#Si existe el fichero /mnt/testigo no se ha montado directorio remoto por sshfs,
#Intentamos montarlo y otra vez y luego preguntamos de nuevo.
#Si no se ha montado nada es que no se puede guardar nada de forma permanente ya que estamos escribiendo en la
#memoria interna del router y eso se llena rapido

test -e /mnt/testigo || monta_sshfs

if test -e /mnt/testigo
then
   montado=0
   #No hay almacenamiento organizado por dias, no vamos a guardar tanto tiempo el fichero.
   destino="/mnt/snapshot"
fi

test -d $destino || mkdir -p $destino

case $1 in

   "start")
        log "Detectado inicio de movimiento $2" 
        email "Subject: Evento camara\r\n\r\nDetectado inicio de movimiento $2"
        #El fichero motion.txt tiene dos finalidades: 1) testigo para indicar que estamos detectando movimiento 
        #                                             2) guarda los nombres de los ficheros de captura de las fotos
        touch /tmp/motion.txt
        ;;
   "picture")
        fichero=$(basename $3)
        log "Guardando imagen $2 : $destino/$fichero" 
     
        #Si estamos en una ráfaga de movimiento detectado, guardamos el nombre de fichero con la foto.
        test -e /tmp/motion.txt && echo "$destino/$fichero" >> /tmp/motion.txt

        #Se guarda el fichero en el almacenamiento destino
        mv "$3" "$destino/$fichero"
  
        #Si no está montado el almacenamiento remoto y vemos que llevamos mas de 10 imagenes en
        #movimiento tenemos que borrarlas y enviarlas ya mismo, para no saturar el espacio.
        if [ $montado -eq 0 ]
        then
           #Si estamos en una ráfaga de movimiento detectado....
           if [ -e /tmp/motion.txt ]
           then
              lineas=$(wc -l /tmp/motion.txt | cut -d" " -f1)
              test $lineas -gt 10 && envia_secuencia $2
           else
              #Si no lo estamos, es una captura *snapshot* regular sin interés, la borramos.
              rm -f "$destino/$fichero"
           fi  
        fi
        ;;
   "end") 
        log "Detectado fin de movimiento $2"
        envia_secuencia $2
        ;;
    *) log "Evento $1"
esac


#Borra fichero "sent" creado por sendmail si existe, para liberar espacio
rm -rf /root/sent
rm -rf /sent

exit 0
Por otro lado, el fichero /etc/rc.local se modifica para crear el fichero /mnt/testigo en el arranque.
# cat /etc/rc.local 
# Put your custom commands here that should be executed once
# the system init finished. By default this file does nothing.

sleep 40

#Desmontamos /mnt por si acaso
umount /mnt

#Borramos /mnt/snapshot local si tuviera algo
rm -rf /mnt/snapshot

#Creamos testigo para detectar que no se ha montado el almacen remoto
touch /mnt/testigo

#Montamos almacen remoto por sshfs
sshfs -o ssh_command="ssh -i /root/.ssh/id_rsa -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" -o nonempty openwrt@172.19.231.4:/home/openwrt /mnt

exit 0
Bueno, pues esto quedá asi hasta el siguiente ciclo de mejoras.

miércoles, 18 de mayo de 2016

Quitar acentos, eñes, etc del nombre de una serie de ficheros.

En mi centro tengo un proceso automático que sube un directorio completo de ficheros a un servidor ftp externo usado para publicación web. Ese servidor web no está muy afinado por su administrador en cuanto a codificación y tiene un problema con los ficheros que tienen en su nombre eñes, acentos y demás hierbas, por lo que para ahorrarme dolores de cabeza prefiero que no suba ningún fichero con esos caracteres en su nombre.

Aunque tengo a los usuarios que crean esos ficheros avisados para que tengan cuidado, es imposible evitar que se les cuele algo, por lo que lo mejor es usar un script que renombre esos ficheros antes de subirlos mediante la herramienta lftp.

Utilizo este código del blog de mi compañero Esteban, quedando así:

find /home/Publicaciones/ -type f -mmin +1 -print0 | while IFS= read -r -d '' -r f
do
  nuevo=$(echo "$f" |   sed  'y/áÁàÀãÃâÂéÉêÊíÍóÓõÕôÔúÚñÑçǪº/aAaAaAaAeEeEiIoOoOoOuUnNcCao/')
  test  "$f" != "$nuevo" &&  (echo "Corrigiendo $f" ; mv "$f" "$nuevo")
done

De esta manera me deshago de todo tipo de carácter que pueda causar problemas recorriendo el directorio /media/Publicaciones. Si el fichero experimenta cambios después de pasar por el sed lo renombro con "mv".

lunes, 16 de mayo de 2016

La conjura de los necios (V): "Parole, parole, parole"

Veamos una reunión de la Comisión de Educación, Empleo y Deportes del 13 de mayo del 2016:



El orden del día era:

COMPARECENCIA, ANTE COMISIÓN, A PETICIÓN DE LOS MIEMBROS DE LA CÁMARA (INF2C-32). De la Consejera de Educación y Empleo, o persona en quien delegue, a los efectos de informar ante la Comisión de Educación, Empleo y Deportes sobre la participación de Coordinadores TIC y Administradores Informáticos en el proceso de informatización de los centros de enseñanza no universitaria sostenidos con fondos públicos. Formulada por el Grupo Parlamentario PODEMOS Extremadura de conformidad con lo previsto en el artículo 202 del Reglamento de la Cámara. R.E. nº 4.270

Los políticos hablan, yo me voy a centrar en puntualizar varios hechos:

Minuto 7:50: el Secretario General (SG) afirma que todo el contrato de equipamiento era de fondos directos FEDER que expiraban a finales de 2015. Eso no es del todo cierto. Al menos dos lotes son contratos hechos por Red.es, los correspondientes a las impresoras de inyección no demandadas por nadie (1 millón de euros), y aunque su financiación sea también europea, no tengo elementos para afirmar o negar que tuviesen tales límites temporales:


Minuto 9:15: en ese momento no, pero ahora si sabemos que hubo una comisión secreta de expertos TIC, con reuniones secretas y deliberaciones, de momento, secretas, que han sido elevadas a la Consejería para su valoración.

Minuto 10:15: los administradores informáticos no hemos tenido, a día 16 de mayo, ni un solo curso de formación sobre el equipamiento TIC recibido. Los CPR están impulsando lentamente seminarios que son autogestionados en contenido y desarrollo por nosotros mismos, sin participación ni apoyo del Servicio TIC.

Minuto 21:10: no son impresoras 3D, son impresoras A3. No se han pedido muchas impresoras por que eran "para 10 años". Se han recibido por lo general entre 5-10 veces mas impresoras de las pedidas. En cambio otras cosas mas necesarias solicitadas en su día, como PC para servicios comunes, no se ha recibido ninguno.

Minuto 23:00: que a mi me conste, ningún CPR ha organizado sesiones informativas para administradores informáticos. A mi intentaron colarme en una reunión sobre pizarras digitales, pero vetaron mi presencia ya que solo era para docentes-TICs.

Minuto 31:00: no contar con un colectivo o enviar material innecesario no es una generalización. Que algo suceda de forma general no quiere decir que se esté generalizando al decirlo. Se generaliza cuando el hecho es anecdótico y se eleva a universal.

Minuto 33:50: el anterior Gobierno empezó a socavar el uso del SW Libre, pero el actual ha seguido en esa senda y la ha ampliado desde Educación a Sanidad. Eso es algo evidente.

Minuto 35:20: el foro CoorTIC se usa cada vez mas....para quejarse, no para encontrar soluciones. Es nuestro muro de las lamentaciones privado, sin un Yahveh al otro lado para leer los papelitos con nuestras quejas.

Minuto 37:20: en infolabs no hay 3 o 4 asuntos pendientes en aulas. No, hay varios problemas técnicos y de material que afectan a todos los centros de manera general y sin solución satisfactoria a día de hoy. En el foro CoorTIC están enumerados. De igual manera que hay asuntos pendientes de resolver en casí todos los lotes.

Minuto 37:42: es falso que las impresoras no usadas se retiraron en su momento. Siguen amontonadas ocupando espacio en los centros. No me consta que se haya recogido ninguna impresora excepto alguna que fue rechazada en el momento de la entrega.

Minuto 39:00: todavía falta por entregar material a los centros, no está todo repartido ni montando en los términos del contrato.

Y eso es lo que hay, todo lo demás:



Salud.


sábado, 14 de mayo de 2016

VirtualBoxing Windows (Parte I)

Bueno, pues aunque la Junta de Extremadura apuesta por Linux, a nosotros nos está obligando a dejar también abierto el acceso Windows en determinadas máquinas de los IES, haga falta o no. El año próximo apostará por la comida sana en los comedores escolares y en consecuencia habrá cubos de pollo KFC junto al menú normal. Libeggggtad de elección ante todo.

Como a nadie de los que deciden se le ocurrió que para administrar un número considerable de puestos Windows y no morir en el intento hace falta un Windows Server, directorio activo y mas herramientas de ese estilo, me veo con que tengo que encontrar una manera cómoda de administrar este tinglado intentando que:

1) Los datos de cada usuario puedan ser guardados en un almacenamiento privado y ubicuo dentro de la red del centro. Los tenemos malacostumbrados con nuestro NFS de Linux a esa facilidad de uso y no van a llevar bien volver a los 90 en materia de almacenamiento de datos.

2) Los Windows se estropean o se contagian de virus con una facilidad pasmosa en un sitio donde se comparten los puestos. Debo garantizar una forma rápida y de coste cero de recuperación de Windows a un estado previo.

3) Muchas veces, con un par de días (o minutos) de antelación te piden instalar un software concreto en una serie de puestos, siendo imposible encontrar un hueco para entrar en el aula y prepararlos todos.

Tras darle una docena de vueltas he llegado a la conclusión de que la mejor solución que se me ocurre es:

1) Ejecutar los Windows en una máquina virtual VirtualBox desde una sesión Linux. Eso permite restaurar un Windows rápidamente a un estado previo o distribuir una imagen nueva con software adicional (simplemente es copiar un fichero .VDI conteniendo el disco duro). Esa distribución se pude hacer incluso por la noche y la restauración de imágenes es inmediata.

2) La máquina virtual será definida a nivel de máquina física, en el directorio /opt. Todos los usuarios que hagan login en la máquina física compartirán maquina virtual, no es operativo que cada usuario tenga una máquina virtual en su home como sería el funcionamiento por defecto de VirtualBox.

Para acceder a ella pondremos un icono con un acceso directo en el panel XFCE, de tal manera que no se llega a ver nunca el entorno VirtualBox completo: pinchas y se abre una ventana que carga un Windows. Adjunto captura con el icono para lanzar Windows desde el panel (el icono azul con la ventanita, que poco original):


3) Puesto que antes de llegar a la máquina virtual el usuario tiene que acceder al Linux con sus credenciales, éste tiene acceso a su $HOME del servidor NFS. Lo que voy a hacer es antes de abrir la máquina virtual mapear el $HOME del usuario sobre la unidad E: del Windows (joder, 2016 y todavía con letras de unidad como el CP/M en los años 70). De esta forma, cuando el usuario de la máquina virtual Windows guarde cosas en la unidad E: lo estará haciendo en un sitio seguro externo a la máquina: su carpeta NFS del servidor central.

Bueno, pues una vez establecidos los principios vamos a lo divertido: manos a la obra, que hay que producir riqueza mas rápido que los políticos producen deuda.

Paso 1) Establecer /opt/... para guardar las máquinas vitruales, usando para ello el fichero /etc/environment ya que permite definir variables globales para todo el sistema, que queda:
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games"
VBOX_USER_HOME="/opt/VirtualBox VMs/VirtualBox"
Debemos reiniciar el sistema para que esto se aplique.

Paso 2) Abrimos VirtualBox y creamos la maquina virtual con un nombre WinX, instalamos en ella Windows, lo activamos con su clave de licencia legal y ponemos todo el software que sea necesario. Al final dentro de /opt debe quedar algo parecido a esta estructura (el run_vbox lo vemos ahora en un rato):
/opt/VirtualBox\ VMs/
    run_vbox
    VirtualBox
        compreg.dat
        selectorwindow.log
        VBoxGuestAdditions_4.3.36.iso
        vbox-ssl-cacertificate.crt
        VBoxSVC.log
        VirtualBox.xml
        VirtualBox.xml-prev
        xpti.dat
    Win10
        Logs
        Win10.vbox
        Win10.vbox-prev
        Win10.vdi
Recoordemos que en Wind10.vdi se encuentra el disco duro de la máquina virtual y será el fichero del que hagamos copias para restaurar el sistema y distribuyamos cuando hayamos añadido algo nuevo al sistema que contiene.

Paso 3) Creamos un script que lanzará de forma automática la máquina virtual con un click, para que el usuario no tenga que hacerlo a través de VirtualBox
# cat /opt/VirtualBox\ VMs/run_vbox
#!/bin/bash

#Poniendo esto  /etc/environment
#export VBOX_USER_HOME="/mnt/VirtualBox VMs/VirtualBox"
#en el arranque lo tendremos para todos los usuarios. Maquinas virtuales en local.

machine="Win10"

running=$(VBoxManage list runningvms | grep $machine)
if  [ -n "$running" ]
then
    zenity --error --text "La máquina $machine ya está funcionando"  
else
   #Si cierran la ventana a lo bruto se hace un shutdown de la máquina. Esto al .vbox de la máquina
   #    <extradataitem name="GUI/LastCloseAction" value="Shutdown"/>
   #o bien con VBoxManage...
   VBoxManage setextradata $machine "GUI/LastCloseAction" "Shutdown"

   #Automontamos la carpeta $HOME en el Windows con el nombre "compartido" en la primera unidad libre (E: normalmente).
   VBoxManage sharedfolder add "$machine" --name "compartido" --hostpath "$HOME" --automount

   #Lanzamos la máquina
   VirtualBox --startvm $machine
fi
Paso 4) Creamos un fichero .desktop para el script anterior:
# cat /usr/share/applications/winx.desktop
[Desktop Entry]
Version=1.0
Name=Windows 8
Comment=Windows 8
Exec="/opt/VirtualBox VMs/run_vbox" 
Terminal=false
Icon=/usr/share/icons/hicolor/win8.png
Type=Application
Categories=Categories=X-XFCE;GTK;Utility;
MimeType=text/html;text/xml;application/xhtml_xml;image/webp;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/ftp;
X-Ayatana-Desktop-Shortcuts=NewWindow;NewIncognito
El icono /usr/share/icons/hicolor/win8.png sería este:

Paso 5) Para colocar el enlace en el panel XFCE y que esté fijo y visible para que todos los usuarios lo tengan a la vista para lanzar el Windows en el momento en que lo deseen utilizo el método descrito en esta entrada:

El fichero con el xfce4-panel.xml quedaría mas o menos (en negrita lo destacable):
<?xml version="1.0" encoding="UTF-8"?>
<channel name="xfce4-panel" version="1.0" unlocked="root">
<property name="panels" type="uint" value="1">
<property name="panel-0" type="empty">
<property name="position" type="string" value="p=8;x=840;y=945"/>
<property name="length" type="uint" value="100"/>
<property name="position-locked" type="bool" value="true"/>
<property name="plugin-ids" type="array">
<value type="int" value="1"/>
<value type="int" value="8"/>
<value type="int" value="9"/>
<value type="int" value="11"/>
<value type="int" value="12"/>
<value type="int" value="13"/>
<value type="int" value="10"/>
<value type="int" value="2"/>
<value type="int" value="3"/>
<value type="int" value="4"/>
<value type="int" value="5"/>
<value type="int" value="6"/>
<value type="int" value="7"/>
</property>
<property name="background-alpha" type="uint" value="80"/>
<property name="background-style" type="uint" value="2"/>
<property name="background-color" type="empty"/>
<property name="background-image" type="string" value="/usr/share/images/PanelWin7.png"/>
<property name="size" type="uint" value="36"/>
<property name="autohide" type="bool" value="false"/>
<property name="length-adjust" type="bool" value="true"/>
<property name="span-monitors" type="bool" value="false"/>
<property name="enter-opacity" type="uint" value="100"/>
<property name="leave-opacity" type="uint" value="100"/>
<property name="mode" type="uint" value="0"/>
<property name="nrows" type="uint" value="1"/>
<property name="disable-struts" type="bool" value="false"/>
</property>
</property>
<property name="plugins" type="empty">
<property name="plugin-1" type="string" value="whiskermenu"/>
<property name="plugin-2" type="string" value="tasklist">
<property name="flat-buttons" type="bool" value="true"/>
</property>
<property name="plugin-3" type="string" value="separator">
<property name="style" type="uint" value="0"/>
<property name="expand" type="bool" value="true"/>
</property>
<property name="plugin-4" type="string" value="systray">
<property name="show-frame" type="bool" value="false"/>
<property name="size-max" type="uint" value="22"/>
<property name="names-visible" type="array">
<value type="string" value="blueman-applet"/>
<value type="string" value="miniaplicación gestor de la red"/>
<value type="string" value="ibus panel"/>
</property>
</property>
<property name="plugin-5" type="string" value="indicator">
<property name="blacklist" type="array">
<value type="string" value="com.canonical.indicator.keyboard"/>
<value type="string" value="com.canonical.indicator.session"/>
<value type="string" value="com.canonical.indicator.datetime"/>
<value type="string" value="libappmenu.so"/>
</property>
<property name="known-indicators" type="array">
<value type="string" value="libapplication.so"/>
<value type="string" value="com.canonical.indicator.power"/>
<value type="string" value="com.canonical.indicator.messages"/>
<value type="string" value="com.canonical.indicator.sound"/>
<value type="string" value="libprintersmenu.so"/>
<value type="string" value="com.canonical.indicator.keyboard"/>
<value type="string" value="com.canonical.indicator.session"/>
<value type="string" value="com.canonical.indicator.bluetooth"/>
<value type="string" value="com.canonical.indicator.datetime"/>
</property>
</property>
<property name="plugin-6" type="string" value="separator">
<property name="style" type="uint" value="0"/>
<property name="expand" type="bool" value="false"/>
</property>
<property name="plugin-7" type="string" value="clock">
<property name="digital-format" type="string" value="%d %b, %H:%M"/>
<property name="mode" type="uint" value="2"/>
</property>
<property name="plugin-8" type="string" value="showdesktop"/>
<property name="plugin-9" type="string" value="places">
<property name="blacklist" type="empty"/>
<property name="known-indicators" type="empty"/>
</property>
<property name="plugin-11" type="string" value="launcher">
<property name="items" type="array">
<value type="string" value="google-chrome.desktop"/>
</property>
</property>
<property name="plugin-12" type="string" value="launcher">
<property name="items" type="array">
<value type="string" value="rayuela.desktop"/>
</property>
</property>
<property name="plugin-13" type="string" value="launcher">
<property name="items" type="array">
<value type="string" value="winx.desktop"/>
</property>
</property>
<property name="plugin-10" type="string" value="actions">
<property name="items" type="array">
<value type="string" value="-lock-screen"/>
<value type="string" value="-switch-user"/>
<value type="string" value="+separator"/>
<value type="string" value="-suspend"/>
<value type="string" value="-hibernate"/>
<value type="string" value="-separator"/>
<value type="string" value="-shutdown"/>
<value type="string" value="-restart"/>
<value type="string" value="-separator"/>
<value type="string" value="+logout"/>
<value type="string" value="-logout-dialog"/>
</property>
<property name="appearance" type="uint" value="0"/>
<property name="invert-orientation" type="bool" value="false"/>
<property name="ask-confirmation" type="bool" value="true"/>
</property>
</property>
<property name="configver" type="int" value="2"/>
</channel>
Paso 6) Con todo ya montado, he hecho vídeo en el que abrimos el Windows y jugamos con el un rato, creando ficheros sobre el $HOME del usuario que está ejecutando todo. Eso garantiza que esos icheros estarán ubicuos en cualquier ordenador de la red y no se quedarán enclaustrados dentro de la máquina virtual Windows. Enlace al vídeo (reproducelo en HD para ver bien los detalles) https://drive.google.com/open?id=0B8np4HndYC-lMmZxWlRSZndIRVE.



Una cuestión interesante es el tema de las licencias. Cada máquina virtual debe tener la licencia correcta, pero si nos dedicamos a copiar VDI de una máquina física a otra, todo va con la misma licencia. ¿Como se soluciona esto?. Sencillo:

  • Aquí contamos como extraer la licencia del Windows que nos viene de serie en cada máquina fisica. Por supuesto, también podemos tener los números de licencia si los hemos comprado por otra vía.
  • Aquí nos cuentan como con un sencillo script .vbs que viene de serie podemos cambiar el código de licencia de un Windows y activarlo fácilmente.

Como colofón me dejo un par de notas que con vías interesantes para explorar en el futuro:
  • Crear un usuario o sesión que abra un VirtualBox a pantalla completa, sin entorno de escritorio gráfico. Todo el escritorio para Windows, pero un Linux por debajo.
  • Encontrar como hacer un mapeo para acceder a los pendrives pinchados en la máquina física.

Con esto podemos decir que todos nuestros usuarios tendrán acceso a Windows en las máquinas donde adoptemos este modelo, les haga falta o no. Ya pueden respirar tranquilas nuestras autoridades.

Dos funciones puppet para instalar paquetes debian.

Muchas veces queremos instalar un paquete en nuestro Linux controlado por puppet y resulta que este paquete no está dentro de los repositorios ya que no es un paquete "oficial", por lo que no podemos usar el recurso package.

En ese caso, la tarea puppet debe llevar el fichero .deb hasta la máquina cliente y luego instalarlo, usando varios "exec" de puppet encadenados. Esto con el tiempo es tan frecuente que he decidido hacer un par de funciones para implementarlo y poder usarlo de forma cómoda.

Vamos allá, el primer define lo he llamado "instala_paquete_local", que coge el paquete ".deb" del directorio "files" del módulo desde donde se invoca la función. En los parámetros decimos el nombre del paquete, su versión y el .deb que lo contiene y la función se encarga de copiar el paquete al cliente e instalarlo si no estaba ya instalado.

root@servidor:/etc/puppet/defines# cat instala_paquete_local.pp 

#Coge un paquete .deb del directorio files del modulo desde donde se llama el define y se instala en el equipo si no está.
# $title = nombre del paquete
# $version = versión del paquete
# $paquete = nombre completo del paquete

#Ejemplo uso dentro de un módulo de /etc/puppet/modules/nombre-modulo/manifests/init.pp:
#   instala_paquete_local {"brother-udev-rule-type1":
#       version=>"1.0.0-1",
#       paquete=>"brother-udev-rule-type1-1.0.0-1.all.deb",
#   }
#El fichero brother-udev-rule-type1-1.0.0-1.all.deb estaría en /etc/puppet/modules/nombre-modulo/files/


define instala_paquete_local($version,$paquete) {

       #$caller_module_name es una builtin variable que identifica el modulo llamante del define

       file {"descarga_$title":
                path => "/var/cache/$paquete",
                owner => root, group => root, mode => 644,
               source => "puppet:///modules/$caller_module_name/$paquete",
                notify => Exec["instala_$title"]
       }
      
       exec { "instala_$title":
              path => "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
              command => "lsof /var/lib/dpkg/lock || (dpkg -i --force-architecture /var/cache/$paquete; apt-get -y -f install)",
              refreshonly => true,
              unless => "dpkg -l |grep $title | grep $version | grep ^ii",
              require => File["descarga_$title"]
       }

}
El segundo define lo he llamado "instala_paquete_web", ya que descarga el paquete ".deb" de una URL que le indicamos como parámetro, siendo el resto de parámetros iguales al caso anterior.
 root@servidor:/etc/puppet/defines# cat instala_paquete_web.pp 
#Descarga un paquete desde una URL web y lo instala si no está instalado en el equipo.
# $title = nombre del paquete
# $version = versión del paquete
# $url = url de descarga

#Ejemplo uso dentro de un módulo de /etc/puppet/modules/nombre-modulo/manifests/init.pp:
#   instala_paquete_web {"brother-udev-rule-type1":
#       version=>"1.0.0-1",
#       url=>"http://servicor-web/ficheros/puppet/brother-udev-rule-type1-1.0.0-1.all.deb",
#   }


define instala_paquete_web($version,$url) {

       exec { "descarga_$title":
           path => "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
           command => "wget -q $url -O /var/cache/$title.deb",
           unless => "dpkg -l | grep $title | grep $version | grep ^ii",
           notify => Exec["instala_$title"]
       }

       exec { "instala_$title":
              path => "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
              command => "lsof /var/lib/dpkg/lock || (dpkg -i --force-architecture /var/cache/$title.deb; apt-get -y -f install)",
              unless => "dpkg -l |grep $title | grep $version | grep ^ii",
              refreshonly => true,
              require => Exec["descarga_$title"]
       }

}

Unos cuantos comentarios:

1) Las dos funciones: junto con el resto de funciones que ya tengamos, estarán /etc/puppet/defines/... del servidor puppet.
2) Si tenemos pkgsync en los clientes no olvidar poner el paquete instalado en el mayhave del cliente.
3) El "lsof /var/lib/dpkg/lock" sirve para comprobar que no está en marcha ningún proceso sobre el sistema de paquetes, asegurando así que la instalación se realiza sin errores.

Bueno, pues a instalar, a instalar, que el mundo se va a acabar.

sábado, 7 de mayo de 2016

Saltar los límites en shell restringidos inyectando comandos Unix

Muchos routers wifi y dispositivos similares tienen un Linux dentro al que podemos acceder por ssh o por telnet, pero una vez allí vemos que tienen un shell o un busybox restringido, que solo deja ejecutar algunos comandos Unix.

Buscando otras cosas sobre un router ADSL Nucomm llegué aquí en donde nos cuentan como saltarnos esa limitación. La idea es inyectar comandos shell de forma parecida a como se puede inyectar SQL en un formulario que no está bien protegido. Por ejemplo, si el comando pwd está permitido pero el comando cat no, podemos intentar:
# pwd & cat /etc/passwd
o bien
# pwd ; cat /etc/passwd
Con sorpresa veremos que ambos comandos se ejecutan (concurrentemente en el primer caso y en serie en el segundo, da igual), de tal forma que tenemos acceso al fichero /etc/passwd del router.

La causa seguramente es que el analizador sintáctico de esa shell limitada es tan cutre que solo comprueba el primer comando introducido para filtrarlo, dando paso al resto sin analizarlo.

Un truco interesante que me apunto para otros router, switch y sistemas embebidos parecidos.

Y con esta pildorita vuelvo a la madriguera, que el trabajo nos tiene muuuuuuy atareados.