domingo, 25 de agosto de 2013

Compilación cruzada para Raspberry Pi desde Windows [y IV]: ejecución remota desde Windows

Hace unas semanas empezamos a ver cómo compilar para Raspberry Pi y desde Windows programas escritos en C o C++, a lo que añadimos un post sobre cómo incluir en el entorno de Windows la biblioteca BCM2835 (que permite acceder a los recursos IO de la RPi desde fuentes en C o C++).  En el último post sobre el tema vimos cómo habilitar Notepad++ mediante su plugin NppExec para poder trabajar de forma más comoda y visual los puntos anteriores.  En su último párrafo nos preguntábamos si podríamos ser más ambiciosos y llevar más allá esta filosofía de obtener resultados con tan sólo pulsar una tecla para, por ejemplo, descargar en la RPi el ejecutable y hacerlo correr tras haberlo compilado.  Ya avanzábamos que la respuesta era afirmativa, que podíamos hacer lo propuesto aunque con algo de trabajo.  

Si sólo quieres empezar a codificar y a ejecutar, ve al final del post y copia el código en un nuevo script de NppExec/Notepad++, e instala putty para Windows (utiliza el instalador putty-0.xx-installer.exe) e introduce sus ejecutables en la variable de entorno PATH.  Pero si quieres algo de diversión, sigue el proceso hasta llegar al script final.  Te aseguro que te servirá en adelante para personalizar tu entorno según tus necesidades o preferencias.

Vamos a hacer en primer lugar un poco de ingeniería de software definiendo los requisitos que necesitamos resolver tras haber terminado de codificar o modificar nuestras fuentes:
  1. Salvar el fichero de código C o C++ que estamos trabajando (ya conseguido en el post anterior)
  2. Compilar el código (también conseguido, como vimos)
  3. Transferir el ejecutable obtenido en el punto anterior a la Raspberry
  4. Otorgar permisos de ejecución al ejecutable, ahora residente en la RPi
  5. Ejecutar el programa bajo el SO de la RPi
Cumplidos los requisitos anteriores mediante los oportunos comandos o utilidades, y si somos capaces de reunir estos últimos en un único script, podremos conseguir todo lo anterior mediante una sóla tecla o entrada de menú.  No obstante, por propia experiencia avanzo que es conveniente añadir algunos matices adicionales:
  • Antes de transferir a la RPi el fichero ejecutable obtenido tras el paso 2, es conveniente matar un posible proceso en ejecución del mismo programa que hayamos lanzado, por ejemplo, antes de modificar su código.  Si no lo hacemos así, la ejecución paralela de dos procesos actuando sobre los mismos recursos nos dará problemas.  Eliminar el proceso es posible hacerlo manualmente, claro, pero será más eficiente si integramos esta acción en nuestra tubería.  Podríamos enumerar este requisito como 2.1.
  • Tras haber matado el proceso según el punto anterior, y aunque no es estrictamente necesario, borramos el ejecutable a sustituir por la nueva compilación.  Enumeramos este requisito como 2.2.  Sobra decir que si se trata de la primera compilación cualquiera de estos dos puntos sobra, pero su ejecución sólo conllevará los correspondientes mensajes de error por no existir ni el proceso a aniquilar ni el fichero a eliminar.
Una vez tenemos claro qué queremos hacer, podemos ponernos manos a la obra:  ya tenemos el código que nos resuelve los requisitos 1 (salvar el código) y 2 (compilación), así que empezamos por el requisito 2.1 (matar un posible proceso de un código anterior ya en ejecución).  Seguro que tenemos claro que el comando de Linux pkill nos sirve para eliminar un proceso dado su nombre.  Es decir, pkill holamundo, por ejemplo, acabaría con un posible proceso en ejecución correspondiente a un ejecutable denominado holamundo (ojo:  en realidad, acabaría con todos los procesos que incluyeran la expresión holamundo en su nombre, por lo que hay que tener cuidado y, desde luego, no ejecutarlo nunca como root).  Pero como estamos trabajando bajo un entorno Windows necesitamos ejecutarlo desde este último SO.  ¿Cómo?  Con putty, una implementación cliente SSH para Windows (entre otros SO) cuyo ejecutable putty.exe nos permitirá establecer una sesión remota con la Raspberry desde Windows.

Habrá que descargar, por tanto, el paquete necesario que se encuentra aquí (utiliza el instalador putty-0.xx-installer.exe).  Tras su instalación en Windows, es conveniente modificar la variable de entorno PATH para que recoja la ruta hasta los ejecutables (normalmente C:\Program Files\PuTTY), lo que evitará tener que teclear la ruta completa cada vez que se acceda a esta utilidades. 

El problema es que la línea de comandos de putty no está diseñada para transferir un único (¿?) comando Linux, sino que debe leerlos de un fichero.  Por lo tanto, nuestro trabajo se complica un tanto, pero nada grave.  Así que tendremos que crear al vuelo un fichero, introducirle el comando o comandos a transferir a la RPi, ejecutar putty para que lea y ejecute remotamente los comandos, y finalmente limpiar la basura para que no nos vayan quedando ficheros por ahí.  Para crear el fichero mediante un comando Windows, ejecutaremos

cmd /c echo sudo pkill nombre_proceso_a_matar >> fichero_de_comandos

La sentencia es sencilla:  utilizamos cmd /c, el comando de comandos de Windows, para emitir un echo que añadirá el comando Linux sudo pkill nombre_proceso_a_matar en el fichero fichero_de_comandos, que por cierto creará si no existe.  Más tarde, una vez cumplida su función, el fichero será eliminado mediante 

cmd /c del fichero_de_comandos

que tiene una construcción semejante, utilizando ahora el comando del, a la que acabamos de ver.

En términos prácticos, tenemos que incluir en nuestro script

cmd /c echo sudo pkill $(NAME_PART) >> $(CURRENT_DIRECTORY)\RPiKill

putty.exe –ssh –pw raspberry –m $(CURRENT_DIRECTORY)\RPiKill pi@192.168.1.118
cmd /c echo del $(CURRENT_DIRECTORY)\RPiKill

donde hemos utilizado las variables NAME_PART para el nombre del ejecutable y CURRENT_DIRECTORY para el directorio actual, mientras que hemos llamado arbitrariamente RPiKill al fichero instrumental que va a registrar este comando.  En la línea de comandos de putty se incluye la contraseña de acceso a la Raspberry, que puede omitirse en el raro caso (en este contexto) de que la seguridad sea importante, aunque con el precio de tener que introducirla manualmente, lo que relativiza la eficiencia de este mecanismo.  Para saber más sobre la ejecución de putty en línea de comandos, puedes visitar esta página.  La dirección de red de la RPi habrá que acomodarla a la realmente usada.  Los puntos suspensivos representan otro código adicional que veremos a continuación.

Avanzaremos ahora más deprisa con el requisito 2.2 (eliminación del ejecutable a sustituir), para lo que utilizaremos

cmd /c echo rm $(NAME_PART) >> $(CURRENT_DIRECTORY)\RPiDel
 
putty.exe –ssh –pw raspberry –m $(CURRENT_DIRECTORY)\RPiDel pi@192.168.1.118
 
El fichero RPiDel lo eliminaremos posteriormente de igual manera a la ya vista.

El siguiente requisito, el número 3, supone la transferencia del fichero ejecutable compilado en Windows para que se ejecute nativamente en el Linux de la RPi.  En los posts anteriores utilizábamos métodos de transferencia cómodos mediante un terminal (SmarTTY, en particular), pero ahora buscamos una utilidad que nos permita realizarlo desde la línea de comandos de Windows.  Esta utilidad es pscp, que se instala cuando instalamos el paquete de putty.  La sentencia genérica para su utilización es
pscp.exe –pw raspberry fichero_a_transferir destino

que para nuestros efectos podemos expandirla a

pscp.exe –pw raspberry $(CURRENT_DIRECTORY)\$(NAME_PART) pi@192.168.1.118

con parecidos comentarios sobre seguridad y expansión de las variables a los efectuados más arriba. 

Ya sólo nos queda tratar los dos últimos requisitos (dar permisos de ejecución y ejecutar el programa), que vamos a tratar simultáneamente.  Para ello, recordaremos que los permisos de ejecución los damos en Linux con (por ejemplo) el comando chmod a+x ejecutable, y que la ejecución de un programa situado en el directorio de trabajo se consigue con ./ejecutable puesto que normalmente, y por motivos de seguridad, no se incluye en la variable de entorno PATH de Linux el directorio actual.  Además, si hemos utilizado la librería BCM2835 (que accede al hardware de la RPi), tendremos que ejecutar como root.  En resumen, en un nuevo fichero instrumental que llamaremos RPiExe incluiremos los dos comandos a transferir a la RPi mediante putty:

cmd /c echo chmod a+x $(NAME_PART) >> $(CURRENT_DIRECTORY)\RPiExe

cmd /c echo sudo ./$(NAME_PART) >> $(CURRENT_DIRECTORY)\RPiExe

putty.exe –ssh –pw raspberry –m $(CURRENT_DIRECTORY)\RPiExe pi@192.168.1.118 

De la misma manera que en los casos anteriores, eliminaremos el fichero RPiExe una vez cumplida su función.

Y esto ha sido todo.  El código completo de nuestro script quedaría:

Pueden eliminarse o modificarse algunos de los pasos, incluyéndolos en otros scripts o ejecuciones externas ordenadas desde dentro del propio Notepad++.  Lo importante es comprender los mecanismos de actuación que nos permite el plugin NppExec, con lo que la flexibilidad de nuestro entorno la tendremos asegurada.


No hay comentarios: