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

lunes, 16 de octubre de 2017

En busca del musthave mínimo.

Como estos días he estado enredando con pkgsync no he podido evitar darme cuenta de que el musthave que tengo en todos los pc es bastante tocho. En su mayoría han sido generados con el script /root/scripts/generar_musthave.sh y por lo que veo tienen mucha redundancia.

Por ejemplo, tener en el musthave los paquetes libreoffice y libreoffice-writer es redundante. Las dependencias del primero implican tener instalado el segundo, por que solo con tener el primero en musthave es suficiente. Sobre las librerías (paquetes lib*) mejor ni hablar: casi todas se instalan de forma automática al cumplir las dependencias de otro paquete de rango superior.

El hecho de tener en musthave solo los paquetes importantes, y no sus dependencias, lo hacen mas manejable y menos susceptible a caer en el "pkgsync installation loop" (esté término me lo acabo de inventar). Este "loop" pasa cuando tenemos en el musthave paquetes que son excluyentes entre si y pkgsync resuelve esta situación esquizofrénica instalando y quitando grupos de paquetes en cada llamada de forma alternativa. Eso no debe ser así, ya que pkgsync debe comportarse de forma idempotente: si no tocamos ningún fichero (mayhave/musthave/maynothave/...) todas las llamadas de pkgsync deben quedar el sistema de paquetes como estaba, invariante.

Por esto y porque me apetecía me puse a mirar como reducir el tamaño del musthave para que esté lo mas puro y mínimo posible. Vamos a ver poco a poco lo que he encontrado:

Primero sacamos una lista ordenada con los paquetes instalados de forma no automática (de esta forma nos libramos de los paquetes tipo librería y otros dependientes). Es lo que se llama paquetes explícitos:
# paquetes_explicitos=$(aptitude search '~i !~M' -F '%p' --disable-columns | sort -u)
De estos paquetes los habrá que se han instalado desde /etc/pkgsync/musthave, también estarán los que se hayan instalado desde /etc/pkgsync/musthave.d por tareas independientes nuestras y, por último, los que se hayan instalado a mano y puesto en /etc/pkgsyc/mayhave para no ser desinstalados. Solo nos interesan los primeros (ya que lo que queremos es aligerar el etc/pkgsync/musthave inicial).
for i in $paquetes_explicitos
do
   echo -n "Paquete $i"
   if grep $i /etc/pkgsync/musthave > /dev/null
   then
        echo " ->instalado por musthave"
        echo $i >> musthave.new        
   else
      if grep $i /etc/pkgsync/mayhave > /dev/null
      then
          echo "  ->instalado por mayhave"
      else
          echo "  ->instalado por musthave.d"
      fi
   fi
done
sort -u -o musthave.new musthave.new
A la salida tenemos en musthave.new los paquetes explícitos que están en el /etc/pkgsync/musthave original. Con esto tenemos una selección bastante buena de paquetes, pero no es suficiente. Si miramos detenidamente en el musthave.new vemos que sigue habiendo redundancias. Hay que ir mas allá y limpiar todos los paquetes de musthave.new que dependan de otros paquetes que estén en ese mismo fichero.

Con el siguiente bucle que recorre musthave.new voy metiendo en musthave.deps todas las dependencias de cada paquete. Para ello uso el comando apt-rdepends (que saca las dependencias y sugerencias de un paquete), aplico un par de filtros para quedarme sólo con las dependencias y si aparece el propio paquete entre sus dependencias lo ignoro (por extraño que parezca, a veces sucede). Por último ordenamos la lista de paquetes dependientes y quitamos los repetidos usando "sort -u":
for i in $(cat musthave.new)
do
   #echo "Dependencias de $i"
   apt-rdepends $i |grep Depends: | awk ' { print $2 } ' | sed "/^${i}$/d" >> musthave.deps
done
sort -u -o musthave.deps musthave.deps
Una vez hecho esto, quitamos de musthave.new todos los paquetes que también estén en musthave.deps mediante "comm -23", borrando los paquetes redundantes y dejando el resultado en musthave.final:
comm -23 musthave.new  musthave.deps  > musthave.final
Todo el script junto queda:
# cat musthave-minimo
#!/bin/bash

cp /dev/null musthave.new
cp /dev/null musthave.deps

paquetes_explicitos=$(aptitude search '~i !~M' -F '%p' --disable-columns | sort -u)
for i in $paquetes_explicitos
do
   echo -n "Paquete $i"
   if grep $i /etc/pkgsync/musthave > /dev/null
   then
        echo " ->instalado por musthave"
        echo $i >> musthave.new        
   else
      if grep $i /etc/pkgsync/mayhave > /dev/null
      then
          echo "  ->instalado por mayhave"
      else
          echo "  ->instalado por musthave.d"
      fi
   fi
done
sort -u -o musthave.new musthave.new

for i in $(cat musthave.new)
do
   #echo "Dependencias de $i"
   apt-rdepends $i |grep Depends: | awk ' { print $2 } ' | sed "/^${i}$/d" >> musthave.deps
done
sort -u -o musthave.deps musthave.deps

comm -23 musthave.new  musthave.deps  > musthave.final

exit 0
Una vez obtenido el musthave.final podemos compararlo con el musthave inicial y con mushave.new usando "wc -l". Veremos que el número de paquetes se ha reducido considerablemente. Esto es un ejemplo sobre un Ubuntu con la imagen LTSP:
# wc -l musthave*
 1837 musthave.deps
  174 musthave.final
 1285 musthave
  501 musthave.new
Como vemos pasamos de 1285 paquetes en el musthave generado con el script generar_musthave.sh a 174 en musthave.final, tras realizar las sucesivas purgas de paquetes dependientes.

Antes de aplicar musthave.final de forma general es conveniente hacer una copia de seguridad /etc/pkgsync/musthave, sobrescribir musthave.final sobre él y ejecutar pkgsync en modo simulación (no hace nada de verdad, simplemente te dice que ficheros quitaría y que ficheros instalaría), para calibrar si es inofensivo.
# cp /etc/pkgsync/musthave /etc/pkgsync/musthave.backup
# cp musthave.final /etc/pkgsync/musthave
# pkgsync -s
Normalmente nos dirá que quiere quitar algunos paquete secundarios raros que no parecen tener mayor importancia. Aquí entra en juego la pericia de cada cual a la hora de evaluar el riesgo de dejarle que quite esos paquetes, pero por mi experiencia y tras investigar el contenido los paquetes propuestos puedo decir que todas las veces me han salido paquetes anodinos, que ni sé que hacen ahí ni los voy a echar de menos cuando se vayan.

Una vez seguros de lo que queremos hacer cogemos aire, nos encomendamos a Odín y ejecutamos "pkgsync" sin mirar atrás. En el peor de los casos, siempre podemos volver a cargar la imagen con clonezilla ;-).

viernes, 13 de octubre de 2017

Cómo montar el pkgsync con control compartido usando puppet.

Cada vez que preparo un tipo nuevo de equipo y tengo que meterle pkgsync para mantener la coherencia de los paquetes de forma centralizada me toca re-estudiarme como está montado. Esta vez voy a apuntarlo aquí a modo de guía.

1. Cómo funciona el pkgsync en máquinas clientes.

Gracias a Esteban tenemos un pkgsync cada vez mas potente y versátil. En los repositorios tenemos la version 1.39:
# apt-cache policy pkgsync
pkgsync:
  Instalados: 1.39
  Candidato:  1.39
  Tabla de versión:
 *** 1.39 0
        500 http://linex.educarex.es/ubuntu/trusty/ trusty/linex amd64 Packages
        500 http://servidor/html/linex2/ubuntu/trusty/ trusty/linex amd64 Packages
        100 /var/lib/dpkg/status
        ....
Hace años que partimos de un pkgsync primitivo basado en solo 3 ficheros (/etc/pkgsync/mayhave, maynothave y musthave) que nos daba muchos problemas de competencia entre lo que nos imponían desde Mérida y lo que queríamos tener cada uno en nuestro centro (¿quién manda si hay un único mayhave?).

Gracias a las mejoras de Esteban hemos evolucionado a un sistema de soberanía compartida, donde hay una parte del mayhave (y compañía) controlada por Mérida y otra controlada por nosotros.

Por ello ahora tenemos tres orígenes de ficheros que se fusionan al ejecutar pkgsync en un único /etc/pkgsync/mayhave.all, el cual toma la función que tenía el antiguo y solitario /etc/pkgsync/mayhave. Estos 3 orígenes son:

  • /etc/pkgsync/mayhave: es controlado por Mérida mediante diversas tareas puestas en nuestro servidor puppet. Si lo modificamos será sobreescrito desde el Puppet Master of Our Universe de la Consejería.
  • /etc/pkgsync/mayhave.ies: es controlado por nosotros en nuestro servidor puppet. Para ello debemos tener una tarea hecha que lo controle.
  • /etc/pkgsync/mayhave.d/*: es como mayhave.ies, pero en este directorio podemos poner varios ficheros que se fusionarán con el resto. Es muy cómodo para hacer grupos de paquetes personalizados según el tipo de equipo (por ejemplo /etc/pkgsync/mayhave.d/mayhave.multimedia y poner dentro una lista de paquetes relacionados con multimedia). También debemos tener una tarea hecha por nosotros que controle estos ficheros.

Of course, esto relatado del mayhave se aplica de forma idéntica para musthave y maynothave.

2. Cómo se distribuyen los ficheros de pkgsync oficiales desde el servidor puppet.

Para evitar tener que ir equipo a equipo, los ficheros vistos en el apartado anterior tienen que controlarse desde el servidor puppet del centro. Hemos convenido en que los ficheros musthave/mayhave/maynothave son controlados desde Mérida por su puppet, mientras que los musthave.ies/mayhave.ies/maynothave.ies y los musthave.d/mayhave.d/maynothave.d están controlados por nosotros por nuestro puppet.

Los controlados por el servidor de la Consejería en Mérida se guardan en la ruta /etc/puppet/files/xubuntu del servidor principal del centro. Si vamos allí, veremos al menos estos ficheros:
mayhave.siatic.i386
musthave.hp360
musthave.infolab
musthave.siatic
musthave.siatic.i386
Para saber que se hace con ellos, busco en las tareas puppet:
root@servidor:/etc/puppet# grep -ir puppetinstituto/files/xubuntu/mayhave * 
manifests/classes/xubuntu/siatic.pp:        source => "puppet://puppetinstituto/files/xubuntu/mayhave.siatic.i386",

root@servidor:/etc/puppet# grep -ir puppetinstituto/files/xubuntu/musthave * 
manifests/classes/xubuntu/notebookHP.pp:        source => "puppet://puppetinstituto/files/xubuntu/musthave.hp360",
manifests/classes/xubuntu/siatic.pp:        source => "puppet://puppetinstituto/files/xubuntu/musthave.siatic.i386",
manifests/classes/xubuntu/infolab.pp:#        source => "puppet://puppetinstituto/files/xubuntu/musthave.infolab",
De lo visto podemos decir que:
  • musthave.hp360: es el musthave que se copia para tablets-portátiles hp360.
  • musthave.infolab: es el musthave para infolabs. Lo cierto es que a día de hoy no se usa, se comentó su inserción por un problema con las tarjetas nvidia y así permanece.
  • mayhave.siatic.i386, musthave.siatic.i386: son el mayhave/musthave que se copia a las Siatic con sistema de 64bits.
  • musthave.siatic: ya no se usa. En su día era el oficial de los siatics, pero había paquetes de xserver que daban problemas y se dejó de insertar.

Como vemos, no hay musthaves/mayhaves/maynothave oficiales para equipos ltsp, workstations, portátiles distintos a los hp360 ni para siatics de 64bits. Se entiende que todos estos equipos tienen un /etc/pkgsync/musthave que viene de serie en su respectiva imagen y de momento la Sección no contempla modificarlos por puppet ya que no hay tarea oficial que se ocupe de actualizarlos.

En el caso de las siatic 64bits ni siquiera traen un musthave inicialmente (lo cual hace que pkgsync no funcione ya que requiere de su existencia para trabajar) y si lo queremos debemos generarlo con el script que aparece en la imagen llamado /root/Primer_arranque/generar_musthave.sh (el cual también es una creación de Esteban).

Por lo general tampoco hay mayhaves y maynothave oficiales para ningún equipo, ya que de momento no ha surgido la necesidad de tenerlos de forma general en todos los centros.

3. Cómo distribuyo los ficheros de pkgsync particulares desde el servidor puppet.

Hasta aquí hemos visto la infraestructura común, ahora veremos como controlar por nuestra parte los mayhave/musthave/maynothave que son de nuestra competencia. Nada de esto lo tenemos por defecto en el servidor del centro y por tanto nos toca montar por nuestra cuenta el módulo puppet y los ficheros que distribuye.

En primer lugar aclarar los musthave.ies/mayhave.ies/maynothave.ies no los uso. Tuvieron su momento cuando teníamos Debian Wheezy pero ahora preferimos la solución mas elegante basada en los ficheros fragmentados a través de musthave.d/mayhave.d/maynothave.d. Si lo montamos bien, podremos tener un musthave general, que se aplica a todos los equipos, y un musthave por cada tipo-use2 de equipo.

En mi caso, en /etc/puppet/files/xubuntu tengo esta estructura de directorios creada:
├── mayhave.d
│   ├── mayhave.infolab
│   ├── mayhave.infolab.profesor
│   ├── mayhave.ltsp.profesor
│   ├── mayhave.notebookHP
│   ├── mayhave.portatil-alumno
│   ├── mayhave.siatic
│   ├── mayhave.workstation
│   ├── mayhave.workstation-i386
│   └── mayhave.xubuntu
├── maynothave.d
│   ├── maynothave.infolab
│   ├── maynothave.infolab.profesor
│   ├── maynothave.ltsp.profesor
│   ├── maynothave.notebookHP
│   ├── maynothave.portatil-alumno
│   ├── maynothave.siatic
│   ├── maynothave.workstation
│   ├── maynothave.workstation-i386
│   └── maynothave.xubuntu
└── musthave.d
    ├── musthave.infolab
    ├── musthave.infolab.profesor
    ├── musthave.ltsp.profesor
    ├── musthave.notebookHP
    ├── musthave.portatil-alumno
    ├── musthave.siatic
    ├── musthave.workstation
    ├── musthave.workstation-i386
    └── musthave.xubuntu
Los ficheros .xubuntu se copian a todo los equipos con ubuntu y los otros según los facter tipo (siatic, infolab, ltsp, workstation, etc) y use2 (profesor/alumno). Para ello uso la siguiente clase puppet basada en la original que compartieron mis compañeros del IES Téllez hace ya tiempo:
# cat /etc/puppet/modules/xubuntu_pkgsync/manifests/init.pp 

#Clase para control de los ficheros básicos para pkgsync de siatic, infolab y notebookHP
import "/etc/puppet/defines/*.pp"

class  xubuntu_pkgsync {

        case $use2 {
                "profesor":  { $esprofe=".profesor" }
                default: { $esprofe = "" }
        }

        #====================================================================================================================
        #Ficheros dependientes del tipo: Se pone un mayhave, musthave, maynothave personalizado en función del tipo y use2 (si es profesor) 

        case $tipo {

                "siatic", "infolab", "notebookHP", "ltsp", "workstation","portatil-alumno", "workstation-i386": {  
                        file { "mayhave.instituto":
                         path => "/etc/pkgsync/mayhave.d/mayhave.$tipo",
                         owner => root, group => root, mode => 644, 
                         source => "puppet:///files/xubuntu/mayhave.d/mayhave.${tipo}${esprofe}",
                         ensure => file,
                        }

                        file { "maynothave.instituto": 
                         path => "/etc/pkgsync/maynothave.d/maynothave.$tipo",
                         owner => root, group => root, mode => 644,
                         source => "puppet:///files/xubuntu/maynothave.d/maynothave.${tipo}${esprofe}",
                         ensure => file,
                        }

                        file { "musthave.instituto": 
                         path => "/etc/pkgsync/musthave.d/musthave.$tipo",
                         owner => root, group => root, mode => 644,
                         source => "puppet:///files/xubuntu/musthave.d/musthave.${tipo}${esprofe}",
                         ensure => file,
                        }
                }
                default: { }

        }

        #====================================================================================================================
        #Musthaves/maynothave/mayhave globales para todos los xubuntus controlados por mi

        # musthave global para todos los xubuntu
        file { "musthave.xubuntu":
                path => "/etc/pkgsync/musthave.d/musthave.xubuntu",
                owner => root, group => root, mode => 644,
                source => "puppet:///files/xubuntu/musthave.d/musthave.xubuntu",
                ensure => file,
        }

        # mayhave global para todos los xubuntu
                file { "mayhave.xubuntu":
                path => "/etc/pkgsync/mayhave.d/mayhave.xubuntu",
                owner => root, group => root, mode => 644,
                source => "puppet:///files/xubuntu/mayhave.d/mayhave.xubuntu",
                ensure => file,
        }

        # maynothave global para todos los xubuntu
        file { "maynothave.xubuntu":
                path => "/etc/pkgsync/maynothave.d/maynothave.xubuntu",
                owner => root, group => root, mode => 644,
                source => "puppet:///files/xubuntu/maynothave.d/maynothave.xubuntu",
                ensure => file,
        } 

        #====================================================================================================================
        #Ficheros mayhave/maynothave principales vacios por defecto si no existen.
        #El musthave debe ser creado por el administrador a mano. No se crea por defecto por su peligrosidad.

        # Por si no existen los ficheros, los creamos inicialmente a vacíos
        file { "mayhave.inicial": 
                path => "/etc/pkgsync/mayhave",
                owner => root, group => root, mode => 644,
                ensure => file,
        }

        file { "maynothave.inicial": 
                path => "/etc/pkgsync/maynothave",
                owner => root, group => root, mode => 644,
                ensure => file,
        }

        #El musthave no se crea por precaucion. Si existe el pkgsync se ejecutaría y puede destrozar el sistema de paquetes.
        #   cada /etc/pkgsync/musthave debe ser creado a mano por el administrador. Si no existe, pkgsync se aborta.

}
Y así es como lo tengo montado, utilizando los facter tipo y use2 para colocar los ficheros en las máquinas correspondientes. Los compañeros de Mérida tienen sus ficheros y yo tengo los míos.

Si quiero aplicar un paquete a todos los Ubuntu lo meto en el fichero *.xubuntu correspondiente. Si quiero aplicarlo a un tipo concreto (por ejemplo en los workstation de 64bits), lo meto en el fichero *.workstation correspondiente. En la siguiente ejecución de puppet en el cliente se copiarán los ficheros desde servidor:/etc/puppet/files/xubuntu y en la posterior ejecución de pkgsync se sincronizarán los paquetes de acuerdo con lo ordenado.

Como se ve, es una solución elegante que permite que trabajar colaborativamente sin machacarnos entre nosotros. Parlem.

jueves, 28 de septiembre de 2017

Más Flash en los libros digitales: Editorial Santillana.

Ya hablamos del Flash en los libros digitales de Oxford. Esta semana me ha tocado lidiar con los libros digitales de francés de la Editorial Santillana: Parachute 1/2 y Lycée A1. Como no podía ser menos, Santillana (que viene a ser el brazo armado en educación del grupo Prisa - comentario a mala baba) también ha apostado por una novedosa tecnología de los 90 basada en Flash para sus libros.

Me llegan varios CD cuyo contenido es:
-rwxrwxr-x 1 root root 11183588 sep 26 10:50 a.out
-rw-r--r-- 1 root root       83 sep 26 10:21 autorun.inf
-rw-r--r-- 1 root root   292782 sep 26 10:21 browser.swf
drwxr-sr-x 4 root root    53248 sep 26 10:21 contenido
drwxr-sr-x 2 root root    36864 sep 26 10:21 curso
-rw-r--r-- 1 root root     1214 sep 26 10:21 exeBrowser.htm
-rwxr-xr-x 1 root root 11476637 sep 26 10:21 exeLinux
drwxr-sr-x 3 root root     4096 sep 26 10:21 exeMac.app
-rw-r--r-- 1 root root  5536668 sep 26 10:21 exeWin.exe
-rw-r--r-- 1 root root     1201 sep 26 10:21 lisez-moi.txt
drwxr-sr-x 2 root root     4096 sep 26 10:21 modulos
drwxr-sr-x 2 root root     4096 sep 26 10:21 recursos
drwxr-sr-x 2 root root     4096 sep 26 10:21 rr_moved
En negrita pongo lo interesante. Hay 4 opciones para lanzar la aplicación: exeLinux (para Linux), exeMac.app (para OSX), exeWin.exe (para Windows) y exeBrowser.htm (para navegadores web).

Después de copiar todo a una carpeta compartida de red (/home/..../dpto/Frances/Parachute_1) intento ejecutarlo de la forma normal, lanzando ./exeLinux. En un sistema de 32 bits se abre sin mayor problema y se ejecuta, pero en un sistema de 64 bits (y son la mayoría en nuestra red) sale esta pantalla en blanco:


Además, para ejecutarlo en 64 bits es necesario instalar antes el paquete libnss3:i386. Está claro que al ejecutable (con "file exeLinux" vemos que es de 32 bits) no le gusta nuestro sistema de 64 bits. Estuve investigando un rato si faltaba alguna librería de 32 bits en alguna ruta concreta o algo similar pero no encontré ninguna pista que me llevase a solucionarlo.

Un compañero me dijo que podía ejecutar la versión exeWin.exe mediante wine (simplemente haciendo wine /home/..../dpto/Frances/Parachute_1/exeWin.exe), ya que al parecer por esa vía funciona. Si no queda otra salida no me importa usar el maravilloso wine, pero prefiero probar otras opciones antes.

La única opción que me quedaba era la opción web, que se supone que es independiente del sistema operativo y de la arquitectura de 32/64 bits de éste. Con:
# firefox /home/..../dpto/Frances/Parachute_1/exeBrowser.htm
Se abre el navegador y se carga Flash, pero otra vez se muestra la página en blanco. Repasando notas vi el epílogo de esta entrada, en la cual comento que para ejecutar Flash con ficheros locales hay que dar permisos de forma explícita en dichas carpetas en la configuración de Flash Player.

Ya sabemos que Flash lleva dos o tres lustros teniendo problemas de seguridad y al final deben haber optado por la misma solución que Microsoft: si no podemos hacer un artefacto seguro al menos lo haremos paranoico.

¿Cómo configuramos esto?. Pues abriendo en un navegador Firefox la URL:
https://www.macromedia.com/support/documentation/es/flashplayer/help/settings_manager04.html
La cual nos carga una aplicación web que permite cambiar la configuración del Flash local de nuestro PC. Es un sistema raro de configuración pero así es como hacen las cosas los de Flash. Una vez allí, tal como contamos en la entrada anterior hacemos:

  • Primero, nos aseguramos de estar la pestaña en "Parámetros de seguridad global".
  • Marcamos "Permitir siempre".
  • Pinchamos en Editar--> Agregar--> Buscar carpeta. Le damos la ruta donde está instalado el Parachute y elegimos la carpeta. En mi caso la ruta es "/home/..../dpto/Frances).


Con esto ya podemos abrir la aplicación por web y ver que carga y ejecuta sin problema, ya que Flash tiene permiso para acceder a la ruta local donde está ubicada. Para facilitar el trabajo al usuario pongo este acceso directo en su escritorio:
# cat Parachute_1.desktop 

#!/usr/bin/env xdg-open

[Desktop Entry]
Name=Parachute 1
Version=4.1.0.0
Type=Application
Terminal=false
StartupNotify=true
Exec=firefox /home/.../dpto/Frances/ParaChute1/exeBrowser.htm
Categories=Education
Icon=/home/.../dpto/Frances/ParaChute1/recursos/intro01.jpg

Usándolo lanzamos el Firefox con la página local y con Flash ya configurado para confiar en dicha ubicación. Ahora si funciona tanto en 32 como en 64 bits.

Ya llegados aquí, estaba intrigado por averiguar dónde guarda Flash esta configuración que se edita de una manera tan rara con la URL externa https://www.macromedia.com/support/documentation/es/flashplayer/help/settings_manager04.html. No está muy documentado, pero la ruta donde se guarda esta configuración es:
~/.macromedia/Flash_Player/macromedia.com/support/flashplayer/sys/settings.sol
El fichero settings.sol está en un formato binario propio de Flash, aunque podemos husmear por encima usando la utilidad strings (ya que cat muestra un galimatías ASCII):
# strings settings.sol
Si queremos editar dicho fichero existen también herramientas que permiten leer un fichero .sol y editarlo, mirar dentro y exportarlo/importarlo a un .json. Una de ellas es Minerva, una aplicación online hecha en Flash que pinta así:


Ya para acabar, si queremos propagar esta configuración guardada de Flash a más usuarios simplemente hay que copiar el fichero settings.sol en la ruta indicada en todos sus homes, sin necesidad de ir configurando manualmente uno a uno. Hasta me ha resultado más cómodo comprimir el directorio ~/.macromedia en un .tar.gz y descomprimirlo tal cual en cada home, de tal forma que se crea el árbol de directorios de .macromedia de forma completa.

Bueno, pues hasta aquí hemos llegado con esto. Nos vemos en la siguiente aventura.

domingo, 10 de septiembre de 2017

Activar un usuario de Windows deshabilitado usando chntpw

Por imperativo legal he tenido que instalar un Windows 7 en una máquina algo antigua. Durante la instalación te pide un nombre de usuario que convertirá en el administrador de la máquina y será con el que por defecto se trabajará. El usuario por defecto es administrador, que te voy a contar...

Bueno, pues despues de hacer esto lo primero es despojar a ese usuario de trabajo de sus permisos de administrador, sacándole del grupo de "Administradores". Tras eso, en dicho grupo solo permanece el usuario "Administrador" que es creado de forma silenciosa durante la instalación. También hay que cambiar la contraseña al usuario "Administrador" por la que queramos que tenga.

Tras esto reiniciamos, vamos a la pantalla de login y vemos ambos usuarios. Pero si intentas acceder con el usuario Administrador te dice que "está deshabilitado". Agarrate: el Windows 7 te permite quedar el sistema sin un administrador válido sin avisarte de ello. ¿Ahora cómo habilito el usuario?.

Bueno, pues la solución está en arrancar con un CD/USB Live de Linux que tenga la herramienta chntpw, ya sea el CD propio o alguno que lo contenga (tipo SystemRescueCD o Ubuntu). Una vez arrancado, montamos la particion de Windows en /mnt (o equivalente) con:
# mount /dev/sda2 /mnt
Normalmente el sistema Windows a partir de su versión 7 está en la partición sda2 o sda3, siendo la sda1 una pequeña partición reservada para hacer el mal a través de UEFI o similar.

Una vez montada, hacemos una copia de seguridad del fichero de configuración de usarios (por si metemos la pata poder restaurar):
# cd /mnt/WINDOWS/System32/config
# cp SAM SAM.backup
Avisamos que alguno de los nombres anteriores podría estar en mayúscula/minúscula, en función del Windows usado.

Una vez hecha la copia de seguridad miramos el fichero con chntpw:
# chntpw /mnt/WINDOWS/System32/config/SAM
chntpw version 0.99.6 110511 , (c) Petter N Hagen

Hive  name (from header): <\SystemRoot\System32\Config\SAM>
ROOT KEY at offset: 0x001020 * Subkey indexing type is: 686c 
File size 65536 [10000] bytes, containing 5 pages (+ 1 headerpage)
Used for data: 298/27544 blocks/bytes, unused: 42/9160 blocks/bytes.


* SAM policy limits:
Failed logins before lockout is: 0
Minimum password length        : 0
Password history count         : 0
| RID -|---------- Username ------------| Admin? |- Lock? --|
| 01f4 | Administrador                  | ADMIN  | dis/lock |
| 01f7 | DefaultAccount                 |        | dis/lock |
| 01f5 | Invitado                       |        | dis/lock |
| 03ea | Usuario                        |        | *BLANK*  |
...
...
Como se ve, el usario Administrador está bloqueado. Para cambiar esa situación (o para borrar la contraseña si no la conocemos o la hemos olvidado), usamos el parámetro "-i" (pongo en negrita la parte interactiva):
# chntpw +i /mnt/WINDOWS/System32/config/SAM
chntpw version 0.99.6 110511 , (c) Petter N Hagen
Hive  name (from header): <\SystemRoot\System32\Config\SAM>
ROOT KEY at offset: 0x001020 * Subkey indexing type is: 686c 
File size 65536 [10000] bytes, containing 5 pages (+ 1 headerpage)
Used for data: 298/27544 blocks/bytes, unused: 42/9160 blocks/bytes.

* SAM policy limits:
Failed logins before lockout is: 0
Minimum password length        : 0
Password history count         : 0

<>========<> chntpw Main Interactive Menu <>========<>

Loaded hives: 

  1 - Edit user data and passwords
      - - -
  9 - Registry editor, now with full write support!
  q - Quit (you will be asked if there is something to save)

What to do? [1] -> 1

===== chntpw Edit User Info & Passwords ====

| RID -|---------- Username ------------| Admin? |- Lock? --|
| 01f4 | Administrador                  | ADMIN  | dis/lock |
| 01f7 | DefaultAccount                 |        | dis/lock |
| 01f5 | Invitado                       |        | dis/lock |
| 03ea | Usuario                        |        | *BLANK*  |

Select: ! - quit, . - list users, 0x - User with RID (hex)
or simply enter the username to change: [Administrador] ---pulso Enter---

RID     : 0500 [01f4]
Username: Administrador
fullname: 
comment : Cuenta integrada para la administracion del equipo o dominio
homedir : 

User is member of 1 groups:
00000220 = Administradores (which has 2 members)

Account bits: 0x0211 =
[X] Disabled        | [ ] Homedir req.    | [ ] Passwd not req. | 
[ ] Temp. duplicate | [X] Normal account  | [ ] NMS account     | 
[ ] Domain trust ac | [ ] Wks trust act.  | [ ] Srv trust act   | 
[X] Pwd don't expir | [ ] Auto lockout    | [ ] (unknown 0x08)  | 
[ ] (unknown 0x10)  | [ ] (unknown 0x20)  | [ ] (unknown 0x40)  | 

Failed login count: 0, while max tries is: 0
Total  login count: 1

- - - - User Edit Menu:
 1 - Clear (blank) user password
 2 - Edit (set new) user password (careful with this on XP or Vista)
 3 - Promote user (make user an administrator)
 4 - Unlock and enable user account [probably locked now]
 q - Quit editing user, back to user select
Select: [q] > 4
Unlocked!

Select: ! - quit, . - list users, 0x - User with RID (hex)
or simply enter the username to change: [Administrador] !

<>========<> chntpw Main Interactive Menu <>========<>

Loaded hives: 

  1 - Edit user data and passwords
      - - -
  9 - Registry editor, now with full write support!
  q - Quit (you will be asked if there is something to save)


What to do? [1] -> q

Hives that have changed:
 #  Name
 0  
Write hive files? (y/n) [n] : y
 0   - OK
Y ya podemos ver que está desbloqueado:
# chntpw /mnt/WINDOWS/System32/config/SAM
chntpw version 0.99.6 110511 , (c) Petter N Hagen

Hive  name (from header): <\SystemRoot\System32\Config\SAM>
ROOT KEY at offset: 0x001020 * Subkey indexing type is: 686c 
File size 65536 [10000] bytes, containing 5 pages (+ 1 headerpage)
Used for data: 298/27544 blocks/bytes, unused: 42/9160 blocks/bytes.


* SAM policy limits:
Failed logins before lockout is: 0
Minimum password length        : 0
Password history count         : 0
| RID -|---------- Username ------------| Admin? |- Lock? --|
| 01f4 | Administrador                  | ADMIN  |          |
| 01f7 | DefaultAccount                 |        | dis/lock |
| 01f5 | Invitado                       |        | dis/lock |
| 03ea | Usuario                        |        | *BLANK*  |
...
...
Si nos hemos fijado en un momento dado nos ha ofrecido la siguiente gama de acciones para realizar sobre el usuario:
 1 - Clear (blank) user password
 2 - Edit (set new) user password (careful with this on XP or Vista)
 3 - Promote user (make user an administrator)
 4 - Unlock and enable user account [probably locked now]
 q - Quit editing user, back to user select
Lo cual nos da una idea de la potencia del comando. Incluye incluso una opción de edición de registro que parece bastante peligrosa y de la que ya habĺe en su día para otra cosa.

Bueno, pues con el entuerto arreglado...¡feliz fin del verano!.

sábado, 12 de agosto de 2017

Listado jerárquico de dispositivos USB

Una cortita: ya vimos en su día cómo listar desde un punto de vista jerárquico los dispositivos PCI de nuestro sistema. Ahora necesitaba hacer lo mismo con los dispositivos USB: conocer el entramado de buses y además saber que ancho de banda tiene cada cúal (es decir, si es USB 1, 2 o 3) y el driver del dispositivo que hubiere conectado.

En esta ocasión el comando es:
# lsusb -t
Veamos ejemplos, empezando por mi PC de sobremesa:
# lsusb -t
/:  Bus 05.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M
/:  Bus 04.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M
    |__ Port 2: Dev 4, If 0, Class=Mass Storage, Driver=usb-storage, 12M
/:  Bus 03.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M
    |__ Port 2: Dev 2, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/8p, 480M
    |__ Port 5: Dev 3, If 0, Class=Mass Storage, Driver=usb-storage, 480M
Ahi vemos los diferentes buses y puertos, como están conectados y la velocidad del puerto: 480M para USB2 (driver ehci), 12M para USB1 (driver uhci)y la de 1.5M corresponde a un ratón.

El lsusb a palo seco para este PC es:
# lsusb
Bus 001 Device 003: ID 058f:6362 Alcor Micro Corp. Flash 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 004: ID 1058:10a8 Western Digital Technologies, Inc. Elements Portable (WDBUZG)
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 002: ID 046d:c050 Logitech, Inc. RX 250 Optical Mouse
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Otro ejemplo, con la Rasperry Pi B:
# lsusb -t
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=dwc_otg/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/3p, 480M
        |__ Port 1: Dev 3, If 0, Class=Vendor Specific Class, Driver=smsc95xx, 480M
        |__ Port 2: Dev 4, If 0, Class=Vendor Specific Class, Driver=ov519, 12M
        |__ Port 2: Dev 4, If 1, Class=Audio, Driver=snd-usb-audio, 12M
        |__ Port 2: Dev 4, If 2, Class=Audio, Driver=snd-usb-audio, 12M
        |__ Port 3: Dev 5, If 0, Class=Mass Storage, Driver=usb-storage, 480M
Se puede ver ahí una webcam que tengo conectada (ov519) así como las tarjetas ethernet (smsc95xx) y audio, que en la Raspberry son dispositivos USB.
xt
Un último ejemplo, con un portátil-tablet:
# lsusb -t
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 5000M
    |__ Port 1: Dev 2, If 0, Class=Mass Storage, Driver=usb-storage, 5000M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/11p, 480M
    |__ Port 2: Dev 2, If 0, Class=Video, Driver=uvcvideo, 480M
    |__ Port 2: Dev 2, If 1, Class=Video, Driver=uvcvideo, 480M
    |__ Port 3: Dev 10, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
    |__ Port 3: Dev 10, If 1, Class=Human Interface Device, Driver=usbhid, 1.5M
    |__ Port 4: Dev 3, If 1, Class=Video, Driver=uvcvideo, 480M
    |__ Port 4: Dev 3, If 0, Class=Video, Driver=uvcvideo, 480M
    |__ Port 5: Dev 4, If 1, Class=Wireless, Driver=btusb, 12M
    |__ Port 5: Dev 4, If 0, Class=Wireless, Driver=btusb, 12M
    |__ Port 6: Dev 5, If 0, Class=Human Interface Device, Driver=usbhid, 12M
    |__ Port 7: Dev 6, If 0, Class=Human Interface Device, Driver=usbhid, 12M
    |__ Port 8: Dev 7, If 0, Class=Vendor Specific Class, Driver=rtsx_usb, 480M
    |__ Port 9: Dev 8, If 0, Class=Human Interface Device, Driver=usbhid, 12M
Aparte de lo anterior, aquí vemos un puerto USB3, con velocidad 5000M y driver xhci.

La mayor utilidad que le saco a este comando es saber que velocidad tiene cada puerto USB de un sistema y como están organizados los distintos hubs usb internos, ya que a veces pinchas un disco duro externo o pendrive en el puerto incorrecto y los trasvases de datos se eternizan. De esta manera podemos identificarlos e incluso etiquetarlos para evitar contratiempos.

Seguimos adelante con el verano, que queda mucho todavía :-).

viernes, 4 de agosto de 2017

Sistema de gestión de turnos y colas.

Hay momentos en que en el centro se forman colas bastante largas, especialmente en época de matriculaciones con la gente de pie durante un largo tiempo. Nos pareció buena idea implementar un sistema de colas con número que tienen en otros sitios, de tal forma que los conserjes irán dando números en orden de llegada, una pantalla visible dirá por que número se va atendiendo y el personal de secretaría podrá incrementar dicho número cuando se quiera pasar al siguiente. La gente podrá estar sentada o salir a dar una vuelta sin tener que estar pendientes de guardar cola.

Empezar desde cero era costoso, pero casi siempre hay alguien que ha abierto camino antes y que nos da todo casi hecho. En este caso es este magnífico blog sobre OpenWrt y otros proyectos interesantes: http://openwrt.tuinstituto.es/sistema-de-gestion-de-colas-de-espera-simple-con-openwrt-y-mqtt.

La infraestructura que necesitamos es:

  • Una pantalla de ordenador en un sitio visible, donde se mostrará el número actual. Dicha pantalla estará conectada a un PC (vale un PC antiguo, una Raspberry PI o una Android Box) capaz de abrir un navegador web, mostrando algo similar a:


  • Un ratón adicional conectado por USB a uno de los PC que ya hay en secretaría. Ese ratón permitirá manipular el número de la cola (botón derecho incrementará el número y botón izquierdo lo decrementará). En el blog de Tuinstituto usan un dispositivo con OpenWRT al que enchufan el ratón para esto, pero yo me lo ahorro y usamos un PC que ya está funcionando al que le ponemos un segundo ratón.
  • Un PC intermedio donde estará la cola que comunica ambos dispostivos y almacena físicamente el número de orden. Este PC no es estrictamente necesario y puede ser tanto el PC de la pantalla como el que maneja la cola, pero en mi caso lo he puesto aparte en uno de los servidores del centro por hacer más completo el sistema y mantener total disponibilidad.

Lo que hace más estimulante para mí esta tarea es el uso de dos prometedoras herramientas que desconocía:

  • mqtt: un protocolo ligerísimo pensado para el uso en IoT (Internet de las Cosas) que permite mandar mensajes mediante colas entre dos puntos de Internet/red local.
  • triggerhappy: un demonio ligerito que permite disparar eventos ante pulsaciones en teclados, ratones y otros dispositivos de entrada.

Parte 1: Implementación de la cola mqtt.

Necesitamos un ordenador donde implementar el servidor mqtt (llamado broker mqtt). Podemos usar servidores públicos que hay en Internet, como los de esta lista para hacer las pruebas, pero es mas correcto implantarlo en un equipo de nuestra confianza.

En Debian el protocolo mqtt se implementa con el paquete mosquitto. La versión que viene de serie con Jessie es un poco anticuada y no soporta que el cliente sea una página web y se comunique por Websockets. Es mas adecuado usar la última versión directamente de su repositorio:
# cat /etc/apt/sources.list.d/mosquitto-jessie.list
deb http://repo.mosquitto.org/debian jessie main
# apt-get update
E instalamos los paquetes:
# apt-get install libmosquitto1 mosquitto mosquitto-clients
# dpkg -l | grep mosqui
ii  libmosquitto1:amd64                   1.4.12-0mosquitto1                   amd64        MQTT version 3.1/3.1.1 client library
ii  mosquitto                             1.4.12-0mosquitto1                   amd64        MQTT version 3.1/3.1.1 compatible message broker
ii  mosquitto-clients                     1.4.12-0mosquitto1                   amd64        Mosquitto command line MQTT clients
Nótese como es la versión 1.4, no la 1.3.4 que viene con Jessie. Ahora ponemos está configuración de puertos y reiniciamos el servidor:
# cat /etc/mosquitto/mosquitto.conf

# Place your local configuration in /etc/mosquitto/conf.d/
#
# A full description of the configuration file is at
# /usr/share/doc/mosquitto/examples/mosquitto.conf.example

pid_file /var/run/mosquitto.pid

listener 1883
protocol mqtt

listener 8000
protocol websockets 

#port 8000

persistence true
persistence_location /var/lib/mosquitto/

log_dest file /var/log/mosquitto/mosquitto.log

include_dir /etc/mosquitto/conf.d
Ponemos a andar el sistema:
# /etc/init.d/mosquitto restart
Vamos a probarlo: nos suscribimos a una cola llamada "numero" indicando la IP del PC donde hemos instalado el Broker mqtt:
# mosquitto_sub -v -h 172.19.X.Y -t "numero"
El sistema queda esperando a que aparezca algo en la cola. Ahora, desde otro terminal u otro PC hacemos:
# mosquitto_pub -h 172.19.X.Y -t "numero" -m "A-02"
El código "A-02" se enviará a la cola "numero" del broker y todos los que estén suscritos recibirán dicho código de forma inmediata. Podremos verlo si nos vamos al terminal donde ejecutamos mosquitto_sub.

Parte 2: Visor de la cola.

Ahora debemos configurar un visor de la cola que se suscriba a ella, lea su contenido y lo muestre en una página web. Básicamente es un html con código javacript conecta con el broker mediante WebSockets (por eso instalamos la versión 1.4 de mosquitto) y muestra el último valor de la cola:
# cat /var/www/turnos/Suturno.html 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head>
  <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Su Turno</title>
<style type="text/css">
html, body {
margin: 0px;
height: 100%;
background-color: #000099;
}
@font-face{ 
 font-family: 'Antonio';
 src: url('Antonio-Regular.ttf') format('truetype');
}
#Pagina {
font-family: 'Antonio', Arial, sans-serif;
margin: 0px;
height: 100%;
}
#cabecera {
font-style: normal;
font-weight: bold;
background-color: #000099;
font-size:7vw;
width: 100%;
color: white;
text-align: center;
background-image: url(img/logo_guadalupe.png);
background-position: left center;
background-repeat: no-repeat;
}
#cuerpo {
color: yellow;
text-align: center;
background-color: black;
font-size:22vw;
font-weight: bold;
width: 100%;
height: 60%;
}
#pie {
font-size:7vw;
background-color: #000099;
font-weight: bold;
width: 100%;
height: 20%;
color: white;
font-style: normal;
text-align: center;
}

</style>
    <script src="mqttws31.js" type="text/javascript"></script>
    <script src="jquery.min.js" type="text/javascript"></script>
    <script src="config.js" type="text/javascript"></script>

    <script type="text/javascript">

    var mqtt;
    var reconnectTimeout = 2000;
    function MQTTconnect() {
 if (typeof path == "undefined") {
  path = '/mqtt';
 }
 mqtt = new Paho.MQTT.Client(
   host,
   port,
   path,
   "web_" + parseInt(Math.random() * 100, 10)
 );
        var options = {
            timeout: 3,
            useSSL: useTLS,
            cleanSession: cleansession,
            onSuccess: onConnect,
//            mqttVersion: 3, 
            onFailure: function (message) {
                $('#status').val("Connection failed: " + message.errorMessage + "Retrying");
                setTimeout(MQTTconnect, reconnectTimeout);
            }
        };

        mqtt.onConnectionLost = onConnectionLost;
        mqtt.onMessageArrived = onMessageArrived;

        if (username != null) {
            options.userName = username;
            options.password = password;
        }
        console.log("Host="+ host + ", port=" + port + ", path=" + path + " TLS = " + useTLS + " username=" + username + " password=" + password);
        mqtt.connect(options);
    }

    function onConnect() {
        $('#status').val('Connected to ' + host + ':' + port + path);
        // Connection succeeded; subscribe to our topic
        mqtt.subscribe(topic, {qos: 0});
        $('#topic').val(topic);
    }

    function onConnectionLost(response) {
        setTimeout(MQTTconnect, reconnectTimeout);
        $('#status').val("connection lost: " + responseObject.errorMessage + ". Reconnecting");

    };

    function onMessageArrived(message) {

        var topic = message.destinationName;
        var payload = message.payloadString;

        $('#cuerpo').html(payload);
    };


    $(document).ready(function() {
        MQTTconnect();
    });
    </script>
</head>
<body>
<div id="Pagina">
<input type='text' id='topic' disabled hidden/>
<input type='text' id='status' size="80" disabled hidden/>
<div id="cabecera">IES VIRGEN DE GUADALUPE</div>
<div id="cuerpo">TURNO</div>
<div id="pie">SU TURNO</div>
</div>
</body>
</html>
Existe un fichero config.js con:
/var/www/turnos# cat config.js 
//host = 'broker.hivemq.com'; // hostname or IP address
//port = 8000;                    // Puerto de tipo Websocket
host = '172.19.X.Y';    // hostname or IP address
port = 8000;                    // Puerto de tipo Websocket

topic = 'numero';  // topic to subscribe to
useTLS = false;
username = null;
password = null;
cleansession = true;
// path = '';
El contenido completo de /var/www/turnos es:
# tree /var/www/turnos/
/var/www/turnos/
├── Antonio-Bold.ttf
├── Antonio-Light.ttf
├── Antonio-Regular.ttf
├── config.js
├── img
│   ├── logo_200_original.png
│   ├── logo_guadalupe.jpg
│   └── logo_guadalupe.png
├── jquery.min.js
├── mosquitto-repo.gpg.key
├── mqttws31.js
└── Suturno.html
Y lo descargaremos de este enlace (habrá que adaptar texto y logo). Este código html estará en cualquier máquina que haga de servidor web en la red del centro. Yo he usado el mismo servidor donde está el broker mqtt, pero podría ser cualquier otro.

En la pantalla que muestra el número simplemente habrá un navegador cargado (que soporte javascript, claro) con la siguiente página abierta:
http://servidor-web/turnos/Suturno.html
Yo tengo un Debian con autologin y con el Firefox abriéndose en la página al iniciar sesión.

Parte 3. Controlador de la cola.

Vamos a la parte final: el PC que controla la cola. Empezamos con 3 scripts que incrementarán, decrementarán y resetearan la cola a 0 (no olviden adaptar la IP).
# cat suma_turno.sh
#!/bin/bash

servidor="172.19.X.Y"
topic="numero"

# Obtenemos el ultimo valor guardado C 1 lee el valor y corta la subscripcion
info=$(mosquitto_sub -t $topic -h $servidor -C 1)

# Metemos en un array la letra y el numero
IFS='-' read -r -a array <<< "$info"
letra="${array[0]}"
num="${array[1]}"

if [ $num = "99" ]; then
nuevo_numero="00"
else # Incrementamos en 1 el valor usando base 10
nuevo_numero=$((10#$num + 1))
fi

# Formatea el numero con ceros delante.
n=`printf %02d $nuevo_numero`

# Publica de forma persistente -r la letra-numero
mosquitto_pub -t $topic -h $servidor -m "$letra-$n" -r

exit 0

# cat resta_turno.sh

#!/bin/bash

servidor="172.19.X.Y"
topic="numero"

# Obtenemos el ultimo valor guardado C 1 lee el valor y corta la subscripcion
info=$(mosquitto_sub -t $topic -h $servidor -C 1)

# Metemos en un array la letra y el numero
IFS='-' read -r -a array <<< "$info"
letra="${array[0]}"
num="${array[1]}"

if [ $num = "00" ]; then
nuevo_numero="99"
else # Incrementamos en 1 el valor usando base 10
nuevo_numero=$((10#$num - 1))
fi

# Formatea el numero con ceros delante.
n=`printf %02d $nuevo_numero`
# Publica de forma persistente -r la letra-numero
mosquitto_pub -t $topic -h $servidor -m "$letra-$n" -r

exit 0
# cat reset_turno.sh

#!/bin/bash

servidor="172.19.X.Y"
topic="numero"

mosquitto_pub -t $topic -h $servidor -m "A-00" -r

exit 0
Notemos que hemos llamado a mosquito_sub con "-C 1": esto se hace asi para leer un valor y desconectar, de lo contrario el script quedaría bloqueado eternamente suscrito a la cola.

Estos scripts podríamos ponerlos como accesos directos en el escritorio del PC de secretaría que controlará el sistema de turno. Pero tal como hacen los compañeros de http://openwrt.tuinstituto.es/ queda mucho más práctico y rápido usar un dispositivo dedicado solo para controlar al menos el incremento/decremento.

Esto se consigue con un ratón adicional y triggerhappy. Pinchamos un ratón adicional en el puerto usb y hacemos:
# ls -l /dev/input/by-id
total 0
lrwxrwxrwx 1 root root  9 jun  8 10:22 usb-0461_USB_Optical_Mouse-event-mouse -> ../event3
lrwxrwxrwx 1 root root  9 jun  8 10:22 usb-0461_USB_Optical_Mouse-mouse -> ../mouse0
lrwxrwxrwx 1 root root  9 jun  8 10:22 usb-CHICONY_HP_Basic_USB_Keyboard-event-kbd -> ../event4
lrwxrwxrwx 1 root root 10 jul 10 13:40 usb-PixArt_HP_USB_Optical_Mouse-event-mouse -> ../event16
lrwxrwxrwx 1 root root  9 jul 10 13:40 usb-PixArt_HP_USB_Optical_Mouse-mouse -> ../mouse1
lrwxrwxrwx 1 root root 10 jun  8 10:22 usb-Sonix_Technology_Co.__Ltd._USB_2.0_Camera-event-if00 -> ../event15
Este listado anterior nos muestra los dispositivos de entrada conectados al sistema. El ratón extra que hemos metido aparece en negrita. Debemos por tanto monitorizar /dev/input/event16 (o /dev/input/by-id/usb-PixArt_HP_USB_Optical_Mouse-event-mouse) y eso lo haremos con el paquete triggerhappy, tal como muestran aquí. Instalamos el paquete:
# apt-get install triggerhappy
Y probamos a mano si funciona:
# thd --dump /dev/input/event16
Moviendo el ratón y pulsando botones veremos como salta la ristra de eventos, lo cual nos sirve para identificar cada evento para luego asociar a él un "trigger". Ahora asociamos los eventos que nos interesan (pulsación de ambos botones)
# cat /etc/triggerhappy/triggers.d/raton.conf
BTN_LEFT    1  /usr/bin/suma_turno.sh
BTN_RIGHT   1  /usr/bin/resta_turno.sh
Esto funcionaría para cualquier ratón conectado al sistema, así que tenemos que filtrar para que solo se haga para el ratón adicional, modificando la configuración del demonio tal como se muestra aquí:
# cat /etc/default/triggerhappy
# Defaults for triggerhappy initscript
# sourced by /etc/init.d/triggerhappy
# installed at /etc/default/triggerhappy by the maintainer scripts

#
# This is a POSIX shell fragment
#

# Additional options that are passed to the Daemon.

#Solo captura eventos del raton HP
DAEMON_ARGS="--daemon --triggers /etc/triggerhappy/triggers.d/ --socket /var/run/thd.socket --pidfile $PIDFILE --user nobody /dev/input/by-id/usb-PixArt_HP_USB_Optical_Mouse-event-mouse"

DAEMON_OPTS=""

# The Triggerhappy daemon (thd) drops its root privileges after
# startup and becomes "nobody". If you want it to retain its root
# status (e.g. to run commands only accessible to the system user),
# uncomment the following line or specifiy the user option yourself:
#
# DAEMON_OPTS="--user root"
Con esto, reiniciando el demonio triggerhappy ya podemos comprobar que al pulsar los botones del ratón añadido se incrementa o decrementa el valor de la cola. Esto tiene un reflejo automático en la pantalla de visualización que está suscrita a ella.

Queda un pequeño detalle: el ratón adicional es fiel a su naturaleza y se comporta como un ratón normal, con lo que al moverlo o pulsarlo tendrá un efecto molesto (ya que se moverá el cursor) en el escritorio del PC al que está conectado. Lo ideal es que solo se dedique a controlar la cola y no tenga efecto sobre el cursor y el escritorio. ¿Podemos desvincularlo del escritorio? Si se puede con este script:
# cat /usr/bin/anula_raton_turnos.sh
#!/bin/bash

id=$(xinput list --id-only "PixArt HP USB Optical Mouse")

if [  ! -z "${id##*[!0-9]*}"  ]   #compruebo si es un número
then
  xinput set-int-prop $id "Device Enabled" 8 0  #Lo desactivo
fi
exit 0
Busca el ratón llamado "PixArt HP USB Optical Mouse" y lo desactiva como dispositivo de las X. Aquí habría que adaptar al nombre del ratón que tenga cada cual. Solo queda hacer que esto se ejecute al iniciar sesión y ese ratón dejará de molestar y quedará para su funcion primordial:
# cat # /etc/xdg/autostart/anula_raton_turnos.desktop
[Desktop Entry]
Encoding=UTF-8
Version=1.0
Type=Application
Exec=/usr/bin/anula_raton_turnos.sh
Icon=
Terminal=false
Name[es_ES]=Anula Raton Turnos
Name=controlIES
Comment[es_ES]=
Coment=
Categories=Application;Education;
Y con esto dejamos el verano fluir y ya si escribo algo será puntual que no quiera dejar pasar. A pasarlo bien...

miércoles, 26 de julio de 2017

Un poquito de HTML: imagen que duplica su tamaño al pasar el ratón.

Pues tarde o temprano hay que meterse en cosas de decoración y retocar alguna página web. En esta ocasión me pedían colgar en una página que usamos como intranet del centro unas miniaturas de otras páginas web y añadir un efecto que hiciera que dichas miniaturas aumentasen de tamaño hasta hacerse bien visibles al pasar el cursor del ratón sobre ellas. Como me ha costado un poco averiguar como hacerlo lo dejo aquí para que conste.

En primer lugar tenemos un script que hace capturas de las páginas en cuestión (utilidad wkhtmltoimage), generando un fichero en formato .png y ajustando luego el tamaño (utilidad mogrify):

#!/bin/bash

DESTINO="/var/www/intranet/images"

/usr/bin/wkhtmltoimage --quality 50 http://iesvdeguadalupe.juntaextremadura.net  $DESTINO/ies-webpage.png
/usr/bin/wkhtmltoimage --quality 50 http://iesvirgendeguadalupecc.blogspot.com.es   $DESTINO/ies-blog.png

/usr/bin/mogrify -crop 800x1000+0+0 $DESTINO/ies-blog.png
/usr/bin/mogrify -crop 800x1000+0+0 $DESTINO/ies-webpage.png

exit 0
Este script es llamado periódicamente desde algún crontab para actualizar ambas imágenes conforme se actualizan las webs.

Ahora nos toca mostrar esas imágenes con el efecto de aumento. Tras refrescar mi CSS y buscar un poco en Internet (lo que me ha servido para confirmar que .css es en algunos aspectos un lenguaje funcional: te le dices donde quieres llegar y el se ocupa del cómo). El estilo definido es:
/*============
DIV ZOOMING
=============*/

#boxcontainer{
 position: relative;
 width:650px;
 height: 400px;
}
.box{
 position:relative;
 float:left;
 width:315px;
 height:100%;
 margin:5px;
}
.Inside{    
 position:absolute;
 top:0;
 left:0;
 width:290px;
 height:100%;
 transition:all 0.5s ease-in-out;
 -webkit-transition:all 0.2s ease-in-out;
}
.Inside:hover{
 z-index:100;
 transform: scale(2);
 transform-origin: top right;
}
Se definen los tamaños de las cajas que contendrán ambas imágenes y luego el efecto:
  • En "Inside": indicamos que habrá una transición (el efecto de aumento) y específicamos el tiempo que tomará en realizarse.
  • En "Inside:hover": se define como cambiará "Inside" cuando el ratón pase por encima ("hover"). Se define un z-index de 100 para que esté en primer plano, por encima del resto, la transformación con un reescalado x2 y la dirección del movimiento partiendo de arriba-derecha (había abajo-izquierda).
Por último el HTML que hace uso de los estilos anteriores, que será lo publicado en la Intranet del centro:
<div id="boxcontainer">
  <div class="box">
    <div class="Inside">Blog
      <a href="http://iesvirgendeguadalupecc.blogspot.com.es/" target="_blank">
        <img style="height: 100%; width: 100%; object-fit: contain;" src="/intranet/images/ies-blog.png" alt="" />
      </a>
    </div>
  </div>
  <div class="box">
    <div class="Inside">Web
      <a href="https://iesvdeguadalupe.educarex.es/" target="_blank">
        <img style="height: 100%; width: 100%; object-fit: contain;" src="/intranet/images/ies-webpage.png" alt="" />
      </a>
    </div>
  </div>
</div>
Tenemos las dos imágenes, apuntando a las webs respectivas, dentro de una caja y con el efecto de aumento implantado usando el estilo "Inside". En la siguiente captura vemos el resultado con una de las imágenes en proceso de expansión:


Bueno, nos vamos pero no del todo: publicaremos alguna cosita curiosa más a lo largo de este raro verano.