Seguridad Wireless - Wifi
Equipos y materiales => Puntos de acceso, routers, switchs y bridges => Openwrt & LEDE => Mensaje iniciado por: seny en 03-01-2015, 16:30 (Sábado)
-
Hola, creo que en este foro pueden haber bastantes de nosotros con ideas para aplicar y ciertos conocimientos de programación en C, pero es complicado encontrar la forma de desarrollarlos dentro de OpenWrt.
Propongo crear una base práctica de desarrollo de aplicaciones para OpenWrt Barrier Breaker, algo muy simple como incluir un "Hello World" creado en "C" dentro de una nueva pestaña de Luci llamada por ejemplo "Aprendizaje".
Yo de momento no sabría hacerlo, hago esta propuesta para comprobar que aceptación podría tener algo así y si habría alguien dispuesto a realizarlo.
Simplemente clickarias en la nueva pestaña y mostraria el mensaje "Hello World" en una nueva pantalla en blanco. ;D
Dispondriamos dentro de la carpeta que usamos actualmente para compilar firmwares para nuestro router, de una nueva carpeta donde encontrariamos lo mínimo para que ese HelloWorld.c funcione y podamos desarrollar lo que queramos, compilandose dentro de nuestro firmware.
Saludos.
-
No sé si te refieres a que helloworld.c se compile y ejecute cuando pulsas la pestaña de luci o a que cuando compilemos, ese programa se incluya en nuestro firmware y se ejecute al pulsar la pestaña.
Para lo primero:
No existe ningún toolchain para desarrollar openwrt dentro de openwrt. La mayor parte de las herramientas de compilación simplemente no están adaptadas para funcionar en openwrt. Es por eso por lo que hemos de compilar en un PC para producir código ejecutable en mips.
He intentado de muchas formas buscar un toolchain adecuado para poder desarrollar en openwrt en C, pero al final lo tuve que dejar porque no he encontrado nada medianamente decente sobre lo que poder empezar.
Alguna gente ha conseguido, en una máquina virtual mips bajo QEMU, con debian mips, compilar programas como MLDonkey, pero hacerlo desde el propio openwrt es por ahora una odisea (si es que se puede hacer).
Si alguien tiene alguna idea o alguna aportación, desde luego que estaría más que agradecido de que la compartiera por aquí.
Para lo segundo, ya hay documentación de cómo hacer pequeñas aplicaciones que generen paquetes instalables: https://forum.openwrt.org/viewtopic.php?id=44846
-
Ya conocia las limitaciones que hay para compilar desde el router y mi propuesta era crear una aplicación práctica de lo que comentan en ese link, añadiendo que funcione desde Luci y ya incluida en Barrier Breaker.
Añadir la carpeta con el helloword.c y todo lo necesario para crear la pestaña "Aprendizaje" en el Luci, incluida en el source que bajamos, poderlo modificar y que forma parte de las opciones del menuconfig que lo incluya o no en la compilación que hagamos desde el PC.
Quizás si para empezar se incluyese eso en el siguiente branch sobre la versión oficial que se realize, es una idea que lanzo a ver que tal y creo que con eso algunos podriamos empezar a investigar.
saludos
-
Ya conocia las limitaciones que hay para compilar desde el router y mi propuesta era crear una aplicación práctica de lo que comentan en ese link, añadiendo que funcione desde Luci y ya incluida en Barrier Breaker.
Añadir la carpeta con el helloword.c y todo lo necesario para crear la pestaña "Aprendizaje" en el Luci, incluida en el source que bajamos, poderlo modificar y que forma parte de las opciones del menuconfig que lo incluya o no en la compilación que hagamos desde el PC.
Quizás si para empezar se incluyese eso en el siguiente branch sobre la versión oficial que se realize, es una idea que lanzo a ver que tal y creo que con eso algunos podriamos empezar a investigar.
saludos
Yo crearía un paquete para tenerlo disponible cuando quisiera de la forma antes mencionada, ya que no creo que vayamos a flashear el router entero cada vez que queramos compilar el paquete. Si lo compilamos como paquete lo podemos instalar y desinstalar las veces que queramos sin perder espacio en la flash (cosa que sí sucederá si lo metemos en la imagen y después volvemos a cargar el paquete).
Para ejecutarlo no hay más que instalar el luci-app-commands y darle a ejecutar desde ahí. Vamos, que la parte de luci ya está hecha... ^-^
-
Menudo lio :D y es dificil de exponer, lo mejor seria que alguien más lo probase..
Da error al compilar y solo he encontrado una forma que lo intenta ya que siguiendo los pasos del link anterior, situando helloworld dentro de openwrt/feeds/packages/utils el feeds install no lo instala, explico como lo hago desde la carpeta inicial vacia:
git clone git://github.com/openwrt-es/openwrt.git -b bb-14.07-rc3
cd openwrt
./scripts/feeds update -a
./scripts/feeds install -a
make prereq
coloco la carpeta helloworld dentro de openwrt/package/utils, solo contiene este Makefile:
include $(TOPDIR)/rules.mk
PKG_NAME:=helloworld
PKG_VERSION:=1.0.0
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)
SECTION:=utils
CATEGORY:=Utilities
TITLE:=$(PKG_NAME)
MAINTAINER:=Mazilo <myEmail.add>
endef
define Package/$(PKG_NAME)/description
This is a hello world package that contains a program to only print "hello world".
endef
CONFIGURE_VARS+= \
CC="$(TOOLCHAIN_DIR)/bin/$(TARGET_CC)"
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/bin
$(CP) $(PKG_BUILD_DIR)/$(PKG_NAME) $(1)/usr/bin
endef
$(eval $(call BuildPackage,$(PKG_NAME)))
creo el helloworld-1.0.0.tar.xz con este Makefile y helloworld.c
Makefile:
helloworld: helloworld.o
$(CC) $(LDFLAGS) helloworld.o -o helloworld
helloworld.o: helloworld.c
$(CC) $(CFLAGS) -c helloworld.c
clean:
rm *.o helloworld
helloworld.c
#include <stdio.h>
int main(void)
{
printf("Hello World\n");
return 0;
}
Lo sutuo dentro de la carpeta que he definido como download folder en advanced configuration del menuconfig.
ejecuto: ./scripts/feeds install helloworld
entro en make menuconfig y aparece helloworld en utilidades, lo selecciono y salgo guardando.
ejecuto: make package/utils/helloworld/compile V=s
manel-5m@manel5m-Inspiron-500m:~/wrtar/openwrt$ make package/utils/helloworld/compile V=s
make[1]: Entering directory `/home/manel-5m/wrtar/openwrt'
make[2]: Entering directory `/home/manel-5m/wrtar/openwrt/package/libs/toolchain'
if [ -f /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install.clean ]; then rm -f /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install.clean; fi; echo "libc" >> /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install
if [ -f /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install.clean ]; then rm -f /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install.clean; fi; echo "libgcc" >> /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install
WARNING: skipping libssp -- package not selected
if [ -f /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install.clean ]; then rm -f /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install.clean; fi; echo "libstdcpp" >> /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install
if [ -f /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install.clean ]; then rm -f /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install.clean; fi; echo "libpthread" >> /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install
if [ -f /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install.clean ]; then rm -f /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install.clean; fi; echo "libthread-db" >> /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install
if [ -f /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install.clean ]; then rm -f /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install.clean; fi; echo "librt" >> /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install
WARNING: skipping libgfortran -- package not selected
if [ -f /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install.clean ]; then rm -f /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install.clean; fi; echo "ldd" >> /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install
if [ -f /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install.clean ]; then rm -f /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install.clean; fi; echo "ldconfig" >> /home/manel-5m/wrtar/openwrt/staging_dir/target-mips_mips32_uClibc-0.9.33.2/pkginfo/toolchain.default.install
make[2]: Leaving directory `/home/manel-5m/wrtar/openwrt/package/libs/toolchain'
make[2]: Entering directory `/home/manel-5m/wrtar/openwrt/package/utils/helloworld'
Makefile:31: *** missing separator. Stop.
make[2]: Leaving directory `/home/manel-5m/wrtar/openwrt/package/utils/helloworld'
make[1]: *** [package/utils/helloworld/compile] Error 2
make[1]: Leaving directory `/home/manel-5m/wrtar/openwrt'
make: *** [package/utils/helloworld/compile] Error 2
-
El makefile del ejemplo no sé para qué versión de openwrt era. Supongo que habrá que adaptar algo.
De todas formas el error está muy localizado. Es la última línea de makefile. ¿Por qué no miras otro archivo makefile y ves si la llamada eval ha cambiado de forma y lo adaptas?
-
Pues si, me ha sido mucho más facil a partir de un existente, concretamente he usado la base de uno sencillo, el usbreset y he podido crear un helloworld que se compila, agradecido por los apuntes.
saludos
-
Propongo crear una base práctica de desarrollo de aplicaciones para OpenWrt Barrier Breaker, algo muy simple como incluir un "Hello World" creado en "C" dentro de una nueva pestaña de Luci llamada por ejemplo "Aprendizaje"...
Hasta ayer, yo tampoco había nunca compilado nada en C para OpenWrt, pero gracias a este documento http://www.componentality.com/res/Step-By-Step-Instruction-To-Run-Apps-On-FlexRoad-HW.en.pdf pude por fin compilar un "Hello World". El documento tiene cinco páginas, está en inglés y tiene un error fácilmente subsanable que chafa la compilación. Todo el proceso se lleva a cabo en cuatro fases. Las tres primeras tienen lugar en el PC, que es donde propiamente se compila. La última fase consiste en la instalación del módulo ejecutable en el router, que es donde va a correr el programa. Compilar en una máquina (PC) para otra de distinta arquitectura (router) recibe el nombre de compilación cruzada. Veamos los detalles.
(Las notas en violeta no figuran en el documento original)
PRIMERA FASE: Instalación de programas en el PC Ubuntu
1.1 Instalación del software de control de versiones (llamado SVN o sub-versión).
En una terminal teclear
sudo apt-get update
sudo apt-get install subversion build-essential
1.2 Crear el subdirectorio openwrt dentro de la carpeta home y abrirlo.
mkdir ~/openwrt
cd ~/openwrt
1.3 Descargar OpenWRT Barrier Breaker.
svn co svn://svn.openwrt.org/openwrt/branches/barrier_breaker
1.4 La descarga de Barrier Breaker crea una nueva carpeta. La abrimos.
cd barrier_breaker
1.5 Descargar e instalar todos los feeds disponibles.
./scripts/feeds update -a
./scripts/feeds install -a
1.6 Obtener una lista de los paquetes que faltan.
make menuconfig
1.7 Si todo va bien, aparecerá un menú de configuración que perfilamos para nuestro router.
Nota. (Ejemplo para el hg556a).
Target System (Broadcom BCM63xx) --->
Subtarget (generic) --->
Target Profile (Huawei HG556a) --->
1.8 Guardar la configuración y salir.
1.9 Generar el entorno de compilación. Desde una terminal, nos posicionamos en el direcorio barrier_breaker.
make
Este proceso es lento. En los PC más antiguos puede tardar varias horas.
NOTA. Aprovecha la potencia de tu PC para acelerar el proceso. Utiliza el parámetro -j seguido del número de procesadores + 1. Ejemplo para una máquina de 4 núcleos:
make -j 5
SEGUNDA FASE: Ensamblado.
2.1 Crear una subcarpeta en para el código fuente dentro de la carpeta openwrt y abrirla.
mkdir ~/openwrt/hello
cd ~/openwrt/hello
2.2 Crear los ficheros hello.c y Makefile (la M en mayúscula).
Nota. Este Makefile es para generar un ejecutable para la máquina en la que se compila (el PC).
touch hello.c
touch Makefile
Editar el fichero hello.c con el código fuente del programa.
#include <stdio.h>
void main()
{
printf("Hello, World!\n");
}
NOTA. El código fuente del documento original da un Warning al compilar. No es grave, pero puedes evitarlo utilizando este otro que, además, es más castizo.
#include <stdio.h>
int main()
{
printf("Ola k ase\n");
return 0;
}
Editar el fichero Makefile (la M en mayúscula) con las instrucciones de compilación.
.SUFFIXES: .tar.gz .c
override CFLAGS += -Wall -O0 -g
hello:hello.c
all: hello hello.tar.gz
%.tar.gz: DIR=$(subst .tar.gz,,$@)
%.tar.gz: %.c
@mkdir -p ./$(DIR)-0.1
@cp $^ Makefile ./$(DIR)-0.1
tar -cz -f $@ ./$(DIR)-0.1
2.3 Compilar el programa y probar que funciona en el PC. Más adelante compilaremos para el router.
Seguimos en la carpeta ~/openwrt/hello. Compilamos y lanzamos.
make all
./hello
NOTA. Esta compilación no tarda nada en realizarse, pero también es posible acelerarla. Para una máquina con cuatro núcleos usa el parámetro -j 5.
make -j 5 all
NOTA. Al ejecutar el programa en el PC (./hello) se visualiza el texto "Hello, World!" Esto indica que el programa compila y se ejecuta bien en el entorno PC. Ahora toca compilar para el router.
2.4 Crear la carpeta "hello" en ~/openwrt/barrier_breaker/feeds/packages/utils y abrirla.
mkdir ~/openwrt/barrier_breaker/feeds/packages/utils/hello
cd ~/openwrt/barrier_breaker/feeds/packages/utils/hello
2.5 Crear un fichero Makefile.
NOTA. La M en mayúscula. En el documento original en inglés está en minúscula y da errores al compilar.
Nota. Este Makefile es distinto al del punto 2.2. Contiene las instrucciones para que el compilador genere un ejecutable específico para la máquina objetivo (el router).
touch Makefile
Edita el fichero Makefile con las instrucciones para la compilación cruzada.
include $(TOPDIR)/rules.mk
PKG_NAME:=hello
PKG_VERSION:=0
PKG_RELEASE:=1
PKG_BUILD_DIR:=$(PKG_NAME)-$(PKG_VERSION).$(PKG_RELEASE)
PKG_SOURCE:=hello.tar.gz
PKG_SOURCE_URL:=file:///home/usuario/openwrt/hello/
include $(INCLUDE_DIR)/package.mk
define Package/hello
SECTION:=utils
CATEGORY:=Utilities
TITLE:=HW
endef
define Package/hello/description
Hello World
endef
define Package/hello/Build/Compile
$(MAKE) -C $(PKG_BUILD_DIR) hello
endef
define Package/hello/install
$(INSTALL_DIR) $(1)/bin/
$(INSTALL_BIN) $(PKG_BUILD_DIR)/hello $(1)/bin/hello
endef
$(eval $(call BuildPackage,hello))
¡ATENCIÓN! En la línea PKG_SOURCE_URL:=file:///home/usuario/openwrt/hello/ asegurarse de cambiar el nombre del usuario por el que realmente tenemos en Linux.
NOTA. No hace falta explicitar el nombre del usuario si se cambia la línea original por esta otra.
PKG_SOURCE_URL:=file:///~/openwrt/hello/
TERCERA FASE. COMPILACIÓN.
3.1 Abrir la carpeta ~/openwrt/barrier_breaker, actualizar e instalar feeds.
cd ~/openwrt/barrier_breaker
./scripts/feeds update -i
./scripts/feeds install hello
3.2 Abrir el menú de configuración.
make menuconfig
3.2 Buscar el paquete "hello" y marcarlo con una [M].
Utilities ----> hello
(pulsar la barra de espacios para marcarlo con una M)
3.3 Salir y guardar los cambios.
(Exit, Exit y Yes)
3.4 ¡Compilar para nuestro router OpenWRT!
make package/hello/compile
NOTA. Esta compilación es rápida, pero también se puede acelerar. Para una máquina con cuatro núcleos usa el parámetro -j 5.
make -j 5 package/hello/compile
Si la compilación termina con éxito, se habrá creado un paquete hello_0-1_brcm63xx.ipk con el ejecutable en la carpeta ~/openwrt/barrier breaker/bin/nuestro_router/packages/packages
CUARTA FASE. Traslado del paquete al router, instalación y prueba.
4.1 Copiar el fichero hello_0-1_brcm63xx.ipk en la carpeta /tmp del router.
NOTA. Desde Windows es cómodo copiarlo con WinSCP. Desde Linux se puede utilizar SCP.
4.2 Abrir una sesión ssh en el router e instalar el paquete.
cd /tmp
opkg install hello_0-1_brcm63xx.ipk
NOTA. En Windows es cómodo instalar desde PuTTY. Desde Linux se puede instalar abriendo una sesión ssh.
ssh root@192.168.1.1
4.3 Desde PuTTY o ssh, ejecutar el programa.
hello
Si todo está bien, aparecerá el mensaje "Hello, World!" ("Ola k ase" en la versión corregida y aumentada).
(http://i.imgur.com/e9v6cQA.png)
Saludos.
-
Gracias raphik, ha quedado mucho más claro el procedimiento.
saludos
-
Añadido al [Índice] Hilos relevantes de OpenWrt (https://foro.seguridadwireless.net/openwrt/(indice)-hilos-relevantes-de-openwrt/)
-
Le he hecho algunas correcciones a la guía porque daba errores en una máquina x64 con Ubuntu 14.04 LTS.
Saludos.
-
No lo entiendo, ayer iba todo bien y hoy no. He hecho cambios en el fichero hello.c.
Lo compilo para el PC y lo lanzo para asegurarme de que funciona.
usuario@PC:~/openwrt/hello$ make all
usuario@PC:~/openwrt/hello ./hello
Cambio de carpeta, actualizo los feeds, selecciono el paquete y lo vuelvo a compilar para el router.
cd ~/openwrt/barrier_breaker
./scripts/feeds update -i
./scripts/feeds install hello
make menuconfig
make package/hello/compile
Traslado el fichero hello_0-1_brcm63xx.ipk a la carpeta /tmp del router, lo instalo y lo ejecuto.
opkg install hello_0-1_brcm63xx.ipk
hello
Y el resultado que me aparece en pantalla es el del primer Hello World que compilé, que no tiene nada que ver con el nuevo código.... Es como si hubiese compilado otra vez el fichero fuente de ayer, lo cual es imposible porque no existe.
¿Alguna pista de lo que está pasando?
Saludos.
-
No lo entiendo, ayer iba todo bien y hoy no. He hecho cambios en el fichero hello.c.
Lo compilo para el PC y lo lanzo para asegurarme de que funciona.
usuario@PC:~/openwrt/hello$ [b]make all[/b]
usuario@PC:~/openwrt/hello [b]./hello[/b]
Cambio de carpeta, actualizo los feeds, selecciono el paquete y lo vuelvo a compilar para el router.
cd ~/openwrt/barrier_breaker
./scripts/feeds update -i
./scripts/feeds install hello
make menuconfig
make package/hello/compile
Traslado el fichero hello_0-1_brcm63xx.ipk a la carpeta /tmp del router, lo instalo y lo ejecuto.
opkg install hello_0-1_brcm63xx.ipk
hello
Y el resultado que me aparece en pantalla es el del primer Hello World que compilé, que no tiene nada que ver con el nuevo código.... Es como si hubiese compilado otra vez el fichero fuente de ayer, lo cual es imposible porque no existe.
¿Alguna pista de lo que está pasando?
Saludos.
Puede ser una tontería, pero ¿has comprobado que en el router no haya más de un "hello" ejecutable? Puede que el que estés ejecutando sea el anterior porque el router lo encuentre antes en el path de ejecución. Pon el path absoluto para ejecutarlo /usr/bin/hello (o donde quiera que lo hayas metido)
Comprueba también que el paquete ipk contiene la versión última que acabas de compilar (se puede abrir con WinRAR).
-
Puede ser una tontería, pero ¿has comprobado que en el router no haya más de un "hello" ejecutable? Puede que el que estés ejecutando sea el anterior porque el router lo encuentre antes en el path de ejecución. Pon el path absoluto para ejecutarlo /usr/bin/hello (o donde quiera que lo hayas metido)
Comprueba también que el paquete ipk contiene la versión última que acabas de compilar (se puede abrir con WinRAR).
Sí, lo ha comprobado. Desinstalo "hello" desde LuCI (System - Software - Pestaña Installed Packages) y borro el paquete hello_0-1_brcm63xx.ipk de la carpeta /tmp. Compruebo que no se ejecuta desde la línea de comados. Incluso lo busco con find. Segurísimo de que no está.
find / -name "hello*"
Borro el paquete ipk del router, hago cambios en hello.c, lo compilo, subo el paquete al router, lo instalo y ¡ZAS! aparece la primera versión de "hello".
Ya reportaré algo si averiguo qué está pasando.
Saludos.
-
Puede ser una tontería, pero ¿has comprobado que en el router no haya más de un "hello" ejecutable? Puede que el que estés ejecutando sea el anterior porque el router lo encuentre antes en el path de ejecución. Pon el path absoluto para ejecutarlo /usr/bin/hello (o donde quiera que lo hayas metido)
Comprueba también que el paquete ipk contiene la versión última que acabas de compilar (se puede abrir con WinRAR).
Sí, lo ha comprobado. Desinstalo "hello" desde LuCI (System - Software - Pestaña Installed Packages) y borro el paquete hello_0-1_brcm63xx.ipk de la carpeta /tmp. Compruebo que no se ejecuta desde la línea de comados. Incluso lo busco con find. Segurísimo de que no está.
find / -name "hello*"
Borro el paquete ipk del router, hago cambios en hello.c, lo compilo, subo el paquete al router, lo instalo y ¡ZAS! aparece la primera versión de "hello".
Ya reportaré algo si averiguo qué está pasando.
Saludos.
Comprueba también el paquete ipk que se genera, a ver si contiene la versión nueva o la vieja.
Intenta borrar el directorio de compilación de hello antes de compilar, por si están quedando residuos de una compilación a otra. Creo que está en build_dir.
Intenta también "make package/hello/clean"
-
No lo entiendo, ayer iba todo bien y hoy no. He hecho cambios en el fichero hello.c.
Lo compilo para el PC y lo lanzo para asegurarme de que funciona.
usuario@PC:~/openwrt/hello$ make all
usuario@PC:~/openwrt/hello ./hello
Cambio de carpeta, actualizo los feeds, selecciono el paquete y lo vuelvo a compilar para el router.
cd ~/openwrt/barrier_breaker
./scripts/feeds update -i
./scripts/feeds install hello
make menuconfig
make package/hello/compile
Traslado el fichero hello_0-1_brcm63xx.ipk a la carpeta /tmp del router, lo instalo y lo ejecuto.
opkg install hello_0-1_brcm63xx.ipk
hello
Y el resultado que me aparece en pantalla es el del primer Hello World que compilé, que no tiene nada que ver con el nuevo código.... Es como si hubiese compilado otra vez el fichero fuente de ayer, lo cual es imposible porque no existe.
¿Alguna pista de lo que está pasando?
Saludos.
Compila con V=s para ver si te da error al compilar, porque a mi en la carpeta feeds no me compila nada.
Una cosa me ha quedado clara con mis pruebas es que compilar source que ya exista o añado nuevo en feeds/packages/ da error.
Lo que compilo sea existente o nuevo en la carpeta package/ no da problema.
saludos
-
Hola, creo que en este foro pueden haber bastantes de nosotros con ideas para aplicar y ciertos conocimientos de programación en C, pero es complicado encontrar la forma de desarrollarlos dentro de OpenWrt.
Está claro que el C es mucho más potente que cualquier script, pero hay situaciones en las que un simple script puede bastar.
Un script tipo "Hello World!" sería algo así:
Fichero /root/hello.sh
#!/bin/sh
echo "Hello, World!"
exit
La línea #!/bin/sh es imprescindible para que hello.sh funcione como un comando. También es necesario asignarle permiso de ejecución:
chmod +x /root/hello.sh
Para ejecutarlo:
/root/./hello.sh
Es posible pasarle parámetros al script. Unas variables especiales recogen el valor de los parámetros. $1 almacena el primer parámetro, $2 el segundo... y así sucesivamente. $0 guarda el nombre del propio script.
#!/bin/sh
case $1 in
ES) echo "¡Hola, Mundo!" ;;
FR) echo "Bonjour le Monde !" ;;
DE) echo "Hallo Welt!" ;;
PT) echo "Olá Mundo!" ;;
IT) echo "Ciao Mondo!" ;;
*) echo "Hello, World!" ;;
esac
exit
Al invocarlo desde la línea de comandos producirá una salida distinta según el parámetro que se le pase:
/root/./hello.sh ES => ¡Hola, Mundo!
/root/./hello.sh PT => Bonjour le Monde
/root/./hello.sh DE => Hallo Welt!
...
También es posible hacer que el script devuelva un valor numérico entre 0 a 255 utilizando la sentencia exit n ( 0 ≤ n ≤ 255 ).
Por lo general un comado devuelve 0 si acaba con éxito y cualquier otro valor en caso contrario.
Linux almacena el valor devuelto en una variable especial llamada $? Veamos un ejemplo. El comando ls <fichero> devuelve 0 si <fichero> existe. Si no existe, devuelve 1.
ls seguroqueesteficheronoexiste
echo $?
echo $?
Aunque parece obvio, es preciso tener en cuenta que $? contendrá el valor retornado por el último comando invocado. En el ejemplo, si el fichero no existe, el primer echo $? devolverá 1 y el segundo un 0, ya que el comando anterior (echo, que no ls) terminó con éxito.
Saludos.
-
Para ilustrar lo que se puede hacer con un script, cuelgo uno que resuelve sudokus. Cualquier sudoku.
Para instalaro:
wget -qO - http://galeon.com/raphik/sudoku.tar | tar -xvf - -C /
Para resolver un sudoku como éste:
----- ----- -----
| 6 3 | 1 | 7 4 |
| 2 | 6 7 | 3 |
| 5 4 | 2 | 1 |
----- ----- -----
| 6 | 8 | 2 |
| 3 1 | 9 | 8 |
| 9 | 1 | 6 |
----- ----- -----
| 9 6 | 8 | 1 2 |
| 1 | 7 2 | 9 |
| 2 7 | 6 | 8 |
----- ----- -----
teclear en la línea de comandos:
/root/sudoku.sh 603010704020607030504020001060800200301090080090001006906080102010702090207060008
Tras unos segundos se visualizará:
Sudoku resuelto en 7 segundos.
----- ----- -----
| 683 | 915 | 724 |
| 129 | 647 | 835 |
| 574 | 328 | 961 |
----- ----- -----
| 765 | 834 | 219 |
| 341 | 296 | 587 |
| 892 | 571 | 346 |
----- ----- -----
| 956 | 483 | 172 |
| 418 | 752 | 693 |
| 237 | 169 | 458 |
----- ----- -----
Saludos.
-
Nuestro router OpenWrt permite alojar páginas web que pueden ser visualizadas desde la red. Los ficheros .html hay que situarlos en la carpeta /www del router.
Un ejemplo sencillo.
/www/hello.html
<html><body>
Hello, World!
</body></html>
Puedes descargar e instalarlo tecleando
wget -qO - http://galeon.com/raphik/hello_web.tar | tar -xvf - -C /
Para visualizarlo, escribe en el navegador de Internet (si fuera necesario, sustituye la IP por la de tu router)
192.168.1.1/hello.html
O click aquí http://192.168.1.1/hello.html
Saludos.
-
Interesante hilo. Trataré de exponer un método si cabe más sencillo. Como el helloworld aburre pondré algo más picante. Nuestra aplicación se llamará tp0set
La cual consiste únicamente en este código
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <mtd/mtd-user.h>
#include <string.h>
#define DATA_OFFSET 20 //the offset of the MTD partition where we will write
#define RAWBYTES_LEN 4
int main()
{
mtd_info_t mtd_info; // the MTD structure
erase_info_t ei; // the erase block structure
int i;
unsigned int rawBytes; //the bytes we want to write
rawBytes = 0x00000000;
unsigned char read_buf[0x20000] = {0x00}; // empty array for reading
int fd = open("/dev/mtd0", O_RDWR); // open the mtd device for reading and
// writing. Note you want mtd0 not mtdblock0
// also you probably need to open permissions
// to the dev (sudo chmod 777 /dev/mtd0)
ioctl(fd, MEMGETINFO, &mtd_info); // get the device info
// dump it for a sanity check, should match what's in /proc/mtd
printf("MTD Type: %x\nMTD total size: %x bytes\nMTD erase size: %x bytes\n",
mtd_info.type, mtd_info.size, mtd_info.erasesize);
lseek(fd, 0, SEEK_SET); // go to the first block
read(fd, read_buf, sizeof(read_buf)); // read and store CFE in read_buf
memcpy(read_buf + DATA_OFFSET, (unsigned char*)&rawBytes, RAWBYTES_LEN); //write some bytes to CFE
ei.length = mtd_info.erasesize; //set the erase block size
for(ei.start = 0; ei.start < mtd_info.size; ei.start += ei.length)
{
ioctl(fd, MEMUNLOCK, &ei);
printf("Eraseing Block %#x\n", ei.start); // show the blocks erasing
// warning, this might print a lot!
ioctl(fd, MEMERASE, &ei);
}
lseek(fd, 0, SEEK_SET); // go back to first block's start
write(fd, read_buf, sizeof(read_buf)); // write our modified CFE
close(fd);
printf("Done!!\n");
return 0;
}
código que estará alojado en un fichero que nombraremos como main.c
y su archivo Makefile (el encargado de compilarlo)
PROGS = tp0set
INSTDIR = $(prefix)/usr/bin
INSTMODE = 0755
INSTOWNER = root
INSTGROUP = root
OBJS = main.o
all: $(PROGS)
$(PROGS): $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@
$(STRIP) $@
%.o: %.c
$(CC) -c $(CFLAGS) $^ -o $@
install: $(PROGS)
$(INSTALL) -d $(INSTDIR)
$(INSTALL) -m $(INSTMODE) -o $(INSTOWNER) -g $(INSTGROUP) $(PROGS) $(INSTDIR)
clean:
rm -f $(PROGS) *.o core
Creamos un directorio, donde sea, llamado src y metemos ahí esos dos ficheros.
Creamos otro directorio llamado tp0set, y allí metemos el directorio src creado anteriormente. Ahora en el directorio tp0set creamos otro Makefile (el encargado de crear el paquete)
#
# Copyright (C) 2015 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=tp0set
PKG_RELEASE:=1
PKG_VERSION:=1.0
include $(INCLUDE_DIR)/package.mk
define Package/tp0set
SECTION:=utils
CATEGORY:=Utilities
TITLE:=Tool for switching main core
endef
define Package/tp0set/description
Tool for switching main core, it writes some bytes to CFE
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Build/Compile
$(MAKE) -C $(PKG_BUILD_DIR) \
$(TARGET_CONFIGURE_OPTS) CFLAGS="$(TARGET_CFLAGS) -I$(LINUX_DIR)/include"
endef
define Package/tp0set/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/tp0set $(1)/usr/bin/
endef
$(eval $(call BuildPackage,tp0set))
este es el makefile que crea el paquete.
Copiamos el directorio tp0set al directorio packages/ de openwrt, quedando algo tal que así:
(http://s16.postimg.org/nsytusfwl/captura5.png)
hacemos make menuconfig y seleccionamos el paquete tp0set. Salimos guardamos
y compilamos el paquete
make package/tp0set/compile V=s
Como se ve los pasos son sencillos, lo complicado viene si queremos crear paquetes más complejos.
Por otra parte para verificar que la cosa compila correctamente en nuestro pc, antes de meternos a crear makefiles, puede bastarnos en este caso el comando:
gcc -o tp0set main.c
PD: Ni se os ocurra ejecutar este programa en vuestro router >:D.
-
Aburre doblemente. Por lo soso y porque no hay forma de compilarlo.
Saludos.
-
Os sigo atentamente, creo que el mundo ya se puede dar por enterado que lo saludamos desde openwrt ;)
Y si ademas de saludarlo le podemos hacer cosquillas para que se ria como en esto último de danitool, jeje mejor que mejor
saludos
-
Propongo crear una base práctica de desarrollo de aplicaciones para OpenWrt Barrier Breaker, algo muy simple como incluir un "Hello World" creado en "C" dentro de una nueva pestaña de Luci llamada por ejemplo "Aprendizaje".
Simplemente clickarias en la nueva pestaña y mostraria el mensaje "Hello World" en una nueva pantalla en blanco. ;D
Este tema cayó en el olvido. Igual ya no interesa. Aún así, expondré algunos avances.
1) Crear una carpeta donde colocar los archivos de nuestras aplicaciones.
mkdir /usr/lib/lua/luci/controller/myapp
2) Agregar opciones al munú de LuCI desde las que lanzar nuestras aplicaciones. Editar /usr/lib/lua/luci/controller/myapp/new_tab.lua y dejarlo tal que así:
module("luci.controller.myapp.new_tab", package.seeall) --notice that new_tab is the name of the file new_tab.lua
function index()
entry({"admin", "new_tab"}, firstchild(), "Aprendizaje", 30).dependent=false -- Opción principal
entry({"admin", "new_tab", "tab_from_cbi"}, cbi("myapp-mymodule/cbi_tab"), "Hello, World!", 1) -- sub-opción 1
entry({"admin", "new_tab", "tab_from_view"}, template("myapp-mymodule/view_tab"), "¡Hola, Mundo!", 2) -- sub-opción 2
end
Rearrancamos el router para poder ver las flamantes nuevas opciones.
(http://i.imgur.com/58XfdQU.jpg)
Por ahora, las opciones están vacías de contenido. Hay dos maneras de agregar contenido: CBI y VIEW-TAB.
3) Agregar código CBI. Son necesarios dos archivos: uno para el código y otro para los datos. Para el primero, hay que crear la carpetar /usr/lib/lua/luci/model/cbi/myapp-mymodule/ y editar un archivo nuevo nombrado /usr/lib/lua/luci/model/cbi/myapp-mymodule/cbi_tab.lua
m = Map("cbi_file", "Hello, World!", "(Más vale tarde que nunca)")
d = m:section(TypedSection, "info", "Un formulario sencillo:")
a = d:option(Value, "name", "Campo 1"); a.optional=false; a.rmempty = false;
return m
Ahora, el archivo de datos /etc/config/cbi_file.
config 'info' 'A'
option 'name' 'OpenWRT'
(http://i.imgur.com/XBHBsFr.jpg)
4) Agregar código view-tab. Sólo se necesita un archivo en la carpeta /usr/lib/lua/luci/view/myapp-mymodule, nombrado /usr/lib/lua/luci/view/myapp-mymodule/view_tab.htm
<%+header%>
<h2><a id="content" name="content">..Hola, Mundo![/url]</h2>
<div class="cbi-map-descr">(M..s vale tarde que nunca)</div>
<legend>Listado simple de dos elementos</legend>
<ul><li> Elemento 1<%=eating%></li>
<li> Elemento 2<%=eating%></li></ul>
<%+footer%>
(http://i.imgur.com/9KBB8cE.jpg)
NOTAS.-
Por lo que parece, para integrar código CBI en LuCI hay que seguir unas normas muy estrictas.
Contrariamente, el códibo view-tab parece simple html al que se le añade las marcas especiales <%+header%> para agregar la barra horizontal de menú y <%+footer%> para el pie de página.
Saludos.
-
Este tema cayó en el olvido. Igual ya no interesa. [...]
El conocimiento va despacio. No por ello es menos interesante y entretenido adquirirlo.
Da gusto, cuando alguien se esfuerza en explicar las cosas, para el resto de los mortales...
Gracias por el aporte raphik.
-
Habría que hacer más pruebas, pero posiblemente LuCI admita todo lo que se quiera incluir dentro de un iframe.
<%+header%>
<h2><a id="content" name="content">..Hola, Mundo!</a></h2>
<iframe src="https://openwrt.org/" width="100%" height="500px"></iframe>
<%+footer%>
(http://i.imgur.com/BdigPgd.png)
-
Habría que hacer más pruebas, pero posiblemente LuCI admita todo lo que se quiera incluir dentro de un iframe.
<%+header%>
<h2><a id="content" name="content">..Hola, Mundo!</a></h2>
<iframe src="https://openwrt.org/" width="100%" height="500px"></iframe>
<%+footer%>
(http://i.imgur.com/BdigPgd.png)
jejeje que bueno ha quedado esto >:(
-
Habría que hacer más pruebas, pero posiblemente LuCI admita todo lo que se quiera incluir dentro de un iframe.
<%+header%>
<h2><a id="content" name="content">..Hola, Mundo!</a></h2>
<iframe src="https://openwrt.org/" width="100%" height="500px"></iframe>
<%+footer%>
(http://i.imgur.com/BdigPgd.png)
Llegas tarde... >:D
Ya había usado esa técnica para integrar phpFileManager con openwrt, o tu script wifi rotator >>> https://foro.seguridadwireless.net/openwrt/wifi-selector-rotator/msg319261/#msg319261 (https://foro.seguridadwireless.net/openwrt/wifi-selector-rotator/msg319261/#msg319261) <<<. ¡¡¡Jejeje!!!
Mira en wselector-luci-0.1-1.zip\usr\lib\lua\luci\view\cbi.
En definitiva, sí se puede hacer. El problema es que si se sabe directamente la URL del iframe, no estará protegido por la contraseña de luci.
-
Llegas tarde... >:D
Ya había usado esa técnica para integrar phpFileManager con openwrt, o tu script wifi rotator >>> https://foro.seguridadwireless.net/openwrt/wifi-selector-rotator/msg319261/#msg319261 (https://foro.seguridadwireless.net/openwrt/wifi-selector-rotator/msg319261/#msg319261) <<<. ¡¡¡Jejeje!!!
Mira en wselector-luci-0.1-1.zip\usr\lib\lua\luci\view\cbi.
:-[
Lo cierto es que en aquellas fechas estaba más por mejorar el funcionamiento de "wrotator" que en su integración en LuCI. De todas formas, más que la integración de aplicaciones en LuCI, lo que interesa en este post es averiguar cómo desarrollar aplicaciones para OpenWRT.
En C se hace complicado, más que nada por el tema de la compilación.
Por otra parte, tampoco es interesante hacer scripts (como wrotator) basados en la shell de BusyBox.
Hace poco supe que LuCI trabaja en lenguaje Lua, así que me puse a aprender Lua. Es un lenguaje interpretado, con una sintaxis muy facilita de aprender y, además, permite el uso de tablas. En fin, un chollo si se compara con la shell de BusyBox.
Éste es un ejemplo de script CGI escrito en Lua manejable desde LuCI. Se trata de una simple tabla de multiplicar. Hay que ponerlo en /www/cgi-bin.
#!/usr/bin/lua
-- Ejemplo de script CGI (Common Gateway Interface) programado en Lua.
io.write("Content-type: Text/html\n\n")
-- la variable local info recoge los valores del formulario POST
local info = io.read('*all')
local tabla = string.sub(info,13)
pagetop = [[
<html>
<head>
<title>
Tabla de multiplicar Web desarrollada en Lua.
</title>
</head>
<body>
¿Qué tabla quieres imprimir?<br />
(La impresión es en pantalla)
<form method="post">
<select name="multiplicar">
<option value="0" selected>(Selecciona un valor:)</option>
]]
io.write(pagetop)
for i = 1 , 10 do
io.write('<option value=\"'..i..'\">'..i..'</option>')
end
pagemiddle = [[
<input type=submit value="¡Tabla!">
</form>
<div>
]]
io.write(pagemiddle)
if #tabla > 0 then
io.write("<h2>La tabla del "..tabla.."</h2>")
for i = 1 , 10 do
local cadena = string.format( "<tt>%2.0f x %2.0f = %3.0f</tt><br>", tabla, i, tabla * i)
cadena = string.gsub(cadena, " ", " ")
io.write(cadena)
end
end
pagebottom = [[
</div>
<br>
Ejemplo escrito en lenguaje Lua y algo de HTML
<br />
2016 :p raphik
</body>
</html>
]]
io.write(pagebottom)
Y éste es el aspecto que presenta.
(http://i.imgur.com/X1yoRbW.jpg)
Saludos.
-
Interesante. Gracias por la info, me dan ganas de probar a hacer alguna cosilla.
Siempre quise hacer un "shoutbox" simple en un router. Para proveer un "portal" wifi abierto que redireccione al shoutbox para enviar comentarios de la gente que se conecte, como un microforo pero sin temas, solo "comments".
Con esto de lua podría hacer algo interesante. Lo del shoutbox por ejemplo lo habían implementado en los firmwares piratebox, pero hecho en python, el cual el firmware en sí requiere de bastante consumo de memoria y recursos, y una implementación difícil de modificar para otros propósitos.
-
Siempre quise hacer un "shoutbox"... sin temas, solo "comments"... lo habían implementado en... python...
Si te apetece probar ésta...
(http://i.imgur.com/cTECKy7.png)
EDITO:
He subido una una versión más elaborada de la ShoutBox. Hay una pequeña explicación de su funcionamiento un poco más abajo. Para descargar y probar:
wget -qO - http://galeon.com/raphik/shout.tar | tar -xvf - -C /
Saludos.
-
Muy bueno raphik
Estuve un buen rato intentando hacerlo funcionar en AA, no me iba, tal vez tuviese algo mal en luci de otras pruebas. En BB a la primera!, muy inmediato y no parece consumir recursos al menos de CPU.
Solo tuve que meter el código en un script situado en /www/cgi-bin/shout, darle permisos de ejecución, entrar la dirección en el navegador
http://192.168.1.1/cgi-bin/shout
Y a funcionar!!! >:(
-
He probado la ShoutBox en un Huawei EchoLife HG556a (version C) con la Chaos Calmer 15.05 Kernel Version 3.18.20. Supongo que debería funcionar en versiones anteriores, ya que Lua está integrado en OpenWRT desde hace mucho tiempo.
Saludos.
-
Lo del nick y/o mensaje en blanco ya está controlado con la función validateForm(). Lo del refresco de mensajes
es algo más complicado... también.
-
La actualización automática de mensajes en todos los clientes complica el código. La descarga / instalación es igual que antes, pero ahora el archivo comprimido contiene tres ficheros:
/www/cgi-bin/shout/shout.lua
/www/cgi-bin/shout/messages.lua
/www/cgi-bin/shout/timer.lua
Para verlo en marcha, desde un navegador de internet: http://192.168.1.1/cgi-bin/shout/shout.lua
Al cargarlo por primera vez, se generan los ficheros:
/root/mensajes.txt
/root/contador.txt
Funcionamiento:
El archivo shout.lua contiene un formulario y dos iframes. En uno de los iframes se carga el archivo messages.lua, encargado de mostrar los mensajes almacenados en el servidor y de actualizar una variable que contiene el ordinal del último mensaje mostrado. El otro iframe no muestra nada por pantalla, es invisible. Sirve para actualizar cada 10 segundos una variable que contiene el ordinal del último mensaje enviado al servidor. Si el último mensaje enviado es mayor que el último mensaje mostrado, se fuerza la recarga de messages.lua.
Saludos.
-
Como complemento interesante, añadir que se puede enviar mensajes a la ShoutBox con el comando wget del sistema operativo, sin necesidad de un navegador de Internet.
Desde el intérprete de comandos de un PC remoto:
wget --spider --post-data "nick=nombre de usuario&message=Mensaje para enviar." http://192.168.1.1/cgi-bin/shout/shout.lua
El parámetro spider evita la descarga innecesaria del archivo shout.lua.
Saludos.
-
Muy interesante raphik, veo que has conseguido al 100% encontrar la solución al tema que propuse ;)
Lua (Luna en portugués) lo tenemos delante con el Luci y no le hacemos el merecido caso, me está tentando dedicarle tiempo a conocerlo, muy sencillo y liviano..
Saludos
-
Hola. Necesito implementar una funcionalidad en Python para Openwrt por mi trabajo de fin de grado y antes de nada quería hacer un programa en Python que me devuelva por ejemplo la IP para comprobar que funciona y familiarizarme con Openwrt .
- ¿ Es compatible Python con Openwrt ?
- ¿ Cualquier aplicación que desarrolle habría que compilarla junto con Openwrt y volver a cargarlo en el router?.
- ¿ Conocéis de algo realizado en Python para Openwrt o algún manual para cómo desarrollar aplicaciones y aplicárselas a Openwrt ?
Muchas gracias por vuestra ayuda
-
Hola. Necesito implementar una funcionalidad en Python para Openwrt por mi trabajo de fin de grado y antes de nada quería hacer un programa en Python que me devuelva por ejemplo la IP para comprobar que funciona y familiarizarme con Openwrt .
- ¿ Es compatible Python con Openwrt ?
- ¿ Cualquier aplicación que desarrolle habría que compilarla junto con Openwrt y volver a cargarlo en el router?.
- ¿ Conocéis de algo realizado en Python para Openwrt o algún manual para cómo desarrollar aplicaciones y aplicárselas a Openwrt ?
Muchas gracias por vuestra ayuda
Si es compatible, está compilado para openwrt... puedes ver algunos de los paquetes que hay en https://downloads.openwrt.org/latest/brcm63xx/generic/packages/packages/
¿no tienes un router con openwrt?
saludos.
-
- ¿ Es compatible Python con Openwrt ?
Sí, lo es.
- ¿ Cualquier aplicación que desarrolle habría que compilarla junto con Openwrt y volver a cargarlo en el router?.
Las aplicaciones escritas en Python no se compilan: Python es un lenguaje interpretado.
- ¿ Conocéis de algo realizado en Python para Openwrt o algún manual para cómo desarrollar aplicaciones y aplicárselas a Openwrt ?
En este mismo foro puedes encontrar la aplicación pelisalacarta, que está escrita en Python. La tienes aquí: https://foro.seguridadwireless.net/openwrt/re-%28desarrollo%29-openwrt-en-comtrend-ar-5387un/
Hay montones de manuales y cursos de Python en la red. Sólo hay que buscarlos (con Google). Éste, en castellano, podría valerte. El capítulo 1 (Preparación del entorno de desarrollo) no es aplicable en OpenWRT. http://www.iaa.es/python/curso-python-para-principiantes.pdf
Saludos.
-
- ¿ Es compatible Python con Openwrt ?
Sí, lo es.
- ¿ Cualquier aplicación que desarrolle habría que compilarla junto con Openwrt y volver a cargarlo en el router?.
Las aplicaciones escritas en Python no se compilan: Python es un lenguaje interpretado.
- ¿ Conocéis de algo realizado en Python para Openwrt o algún manual para cómo desarrollar aplicaciones y aplicárselas a Openwrt ?
En este mismo foro puedes encontrar la aplicación pelisalacarta, que está escrita en Python. La tienes aquí: https://foro.seguridadwireless.net/openwrt/re-%28desarrollo%29-openwrt-en-comtrend-ar-5387un/
Hay montones de manuales y cursos de Python en la red. Sólo hay que buscarlos (con Google). Éste, en castellano, podría valerte. El capítulo 1 (Preparación del entorno de desarrollo) no es aplicable en OpenWRT. http://www.iaa.es/python/curso-python-para-principiantes.pdf
Saludos.
Muchas gracias raphik. Creo que uno de los principales problemas es no saber cómo trabajar con Openwrt. Es decir, programar una aplicación y poder integrarla . Tampoco encuentro mucho manual de inicializacion en Openwrt.
Saludos
-
.. Creo que uno de los principales problemas es no saber cómo trabajar con Openwrt. Es decir, programar una aplicación y poder integrarla . Tampoco encuentro mucho manual de inicializacion en Openwrt...
Aquí todas y todos hemos pasado por eso.
OpenWrt es Linux. Una versión de Linux capaz de correr sobre determinados routers.
(http://i.imgur.com/Vr53aeN.jpg)
Lo primero que vas a necesitar es hacerte con uno de esos routers. La lista de máquinas compatibles con OpenWrt la tienes aquí: https://wiki.openwrt.org/toh/start
Lo más barato es rebuscar por los cajones de casa, que igual tienes alguno por ahí perdido... O adquirirlo en una tienda de artículos de segunda mano. También podrías comprárselo a alguien del foro que tenga alguno de sobra.
Una vez tengas el router, lo siguiente es descargar la versión adecuada de OpenWrt e instalarla en la máquina. El procedimiento de instalación varía según qué router. Para mi gusto, los más sencillos de instalar son el HG553, HG556A y el Comtrend AR-5387un. Hay guías de cómo se instala en la página de hilos relevantes (https://foro.seguridadwireless.net/openwrt/(indice)-hilos-relevantes-de-openwrt/).
Luego toca acceder al router desde un PC. El router no tiene pantalla, ni teclado, ni ratón. Hay que manejarlo desde un PC con Linux o Windows (supongo que también se podrá con OS X). Básicamente, se trata de abrir en el PC una sesión remota que permitirá manejar el router.
Después hay que instalar en el router el intérprete de Python.
Ahora viene lo de programar en Python. Si has programado en Python para tu PC, también sabrás hacerlo para tu router OpenWrt. La única diferencia es que el fichero que contiene las instrucciones Python está ubicado en el router y no en el PC.
Saludos.
-
Muchas gracias Raphik. Con tus indicaciones me has despejado muchas dudas.
Respecto al router, tengo el Linksys 1900ACS que soporta openwrt. He encontrado el firm que le tocaría y procederé a instalarlo.
Una vez lo tenga, y como prueba, quiero crear un código básico en python que me diga por ejemplo que IP tengo asignada. Entiendo que en en un sistema operativo de escritorio ejecutaría ese fichero en python, pero en el router, ¿ se ejecutaría sólo?.
La intención de mi proyecto de fin de carrera es la de integrar Latch en openwrt para gestionar remotamente el router. Entonces, no se como podría cualquier programa que cree en python ejecutarlo en el router o si se puede añadir alguna funcionalidad a la interfaz del router, como por ejemplo, una pestaña, botón, etc.
Saludos.
-
... Una vez lo tenga, y como prueba, quiero crear un código básico en python que me diga por ejemplo que IP tengo asignada. Entiendo que en en un sistema operativo de escritorio ejecutaría ese fichero en python, pero en el router, ¿ se ejecutaría sólo? ...
Primero, un poco de teoría.
El usuario se comunica con el ordenador mediante el intérprete de comandos. Éste suele disponer de dos tipos de interfaces de usuario (shell): el de línea de texto (CLI, Command-Line Interface) y el gráfico (GUI, Graphical User Interface), también llamado escritorio.
En un router, capacidad de proceso y memoria son limitadas. OpenWrt no podría mover una interfaz gráfica tipo Gnome o KDE. No obstante, cuenta con una interfaz gráfica Web llamada LuCI (https://wiki.openwrt.org/doc/howto/luci.essentials). Por supuesto, también cuenta con una interfaz de línea Almquist Shell (ASH) proporcionada por BusyBox que implementa los comandos más usuales de UNIX.
Ahora, la práctica.
El script más sencillo es el típico Hola Mundo, que en Python será:
Contenido del fichero holamundo.py
print ("Hola Mundo!")
Para ejecutarlo desde la línea de comandos:
phython holamundo.py
Si se quiere ejecutar invocándolo directamente, la primera línea del fichero tiene que informar de cómo tratar el resto del contenido:
Contenido del fichero holamundo.py
#! /usr/bin/env python
print ("Hola Mundo!")
Además, habrá que activarle el atributo de "ejecutable":
chmod 777 holamundo.py
Y, finalmente, ejecutarlo:
./holamundo.py
Para ejecutar un comando automáticamente al arrancar el router, se edita el fichero /etc/rc.local y se le añade una línea que invoque dicho comando.
La IP externa se puede averiguar desde la línea de comandos sin necesidad de Python:
wget -q -O- icanhazip.com
Saludos.
-
Muchas gracias Raphik por tu ayuda. Me has allanado mucho el camino. Voy a intentarlo.
Saludos.
-
Disculpa Raphik te vuelva a molestar. Me he quedado atascado con la instalación. Te comento los pasos que he realizado:
1.- Conecté el router (linksys 1900ACS) mediante el cable ethernet al puerto ethernet del macbook
2.- Accedí al router a través del navegador e instale la imagen de openwrt que correspondía.
3.- A través de consola en OS X he accedido al router mediante ssh y me aparece el menú de inicio de openwrt igual que el de la imagen que pusiste.
4.- Ahora debería instalar Luci, sin embargo, no tengo conexión a internet. Al estar el router conectado al puerto ethernet del macbook, el portátil queda lógicamente sin poder acceder a la web. Entiendo que debería configurar una ip para el router dentro del rango que corresponda para así desconectar el router del puerto ethernet del portátil y que tenga conexión vía wifi.
No obstante, no se si se instaló correctamente openwrt pues no consigo mediante consola introduciendo comandos que me diga por ejemplo la versión de Linux instalada.
También me gustaría saber como es posible volver al firm original de linksys para que quede todo como de fábrica por si tuviera algún problema.
Gracias por tu ayuda.
-
Hola, al igual que te recomendé este hilo para el tema programación, estas últimas dudas que planteas se escapan del objetivo de este hilo, deberias solucionarlas leyendo los manuales que hay en los hilos enchinchetados como por ejemplo "[FAQ] Preguntas más comunes sobre OpenWrt" o "[Índice] Hilos relevantes de OpenWrt".
También te recomiendo usar un router mas sencillo para empezar.. sobre dejarlo como de fábrica, seguro que en la web del fabricante encontrarás su último firmware.
Saludos
-
Hola Seny. En primer lugar, siento si he realizado preguntas ya tratadas pero créeme que he leído los hilos de preguntas frecuentes pero no encuentro lo que realmente estoy buscando.
Respecto al router, no puedo devolverlo. Por lo menos debería restaurar a estado de compra. El firm del fabricante lo tengo pero no sé cómo ponerlo y de esto no he encontrado nada.
Gracias
-
Hola Seny. En primer lugar, siento si he realizado preguntas ya tratadas pero créeme que he leído los hilos de preguntas frecuentes pero no encuentro lo que realmente estoy buscando.
Respecto al router, no puedo devolverlo. Por lo menos debería restaurar a estado de compra. El firm del fabricante lo tengo pero no sé cómo ponerlo y de esto no he encontrado nada.
Gracias
Los hilos relevantes sirven para algo: [Índice] Hilos relevantes de OpenWrt (https://foro.seguridadwireless.net/openwrt/%28indice%29-hilos-relevantes-de-openwrt/)
Léete la configuración del Hg553, que tienes un muy buen ejemplo de cómo hacerlo.
Con respecto a dejar un router con el firmware de fábrica, no siempre es posible, y otras veces hay que hacer pasos adicionales. Si tu intención era devolverlo, deberías haber buscado la posibilidad de volver a la configuración de fábrica, antes de toquetearlo.
De todas formas, google es tu amigo. Busca si alguien lo ha hecho ya, con ese modelo o alguno similar.
Edito: En la página oficial de openwrt, tienes un ejemplo para reflashear desde openwrt al firmware OEM: https://wiki.openwrt.org/toh/linksys/wrt_ac_series (https://wiki.openwrt.org/toh/linksys/wrt_ac_series)
-
Perdonad por ser ignorante en el tema,
pero me gustaría saber porque es tan complicado utilizar C o compilar C utilizando un router de procesador basado en MIPS , si este ha sido capaz de trabajar con Openwrt que es 100% linux y Según me han contado linux esta basado en C, ahora no entiendo donde viene tanta dificultad o problemas, porque siguiendo la lógica tendría que ser fácil pero por curiosidad me gustaría saber el porque.
-
Perdonad por ser ignorante en el tema,
pero me gustaría saber porque es tan complicado utilizar C o compilar C utilizando un router de procesador basado en MIPS , si este ha sido capaz de trabajar con Openwrt que es 100% linux y Según me han contado linux esta basado en C, ahora no entiendo donde viene tanta dificultad o problemas, porque siguiendo la lógica tendría que ser fácil pero por curiosidad me gustaría saber el porque.
Intenta meter todo eso que tú dices en 8mb de disco duro y 32mb de ram, y luego comentas... >:D
Aparte de eso, se compila en un PC, para ejecutar en otro tipo de procesadores (MIPS), porque compilar requiere muchísimos recursos (crosscompiling).
-
Si lo que dices lo entiendo, pero por lo de memoria de disco se puede aumentar utilizando memoria externa con SD o memoria usb.
Por parte de ram es algo ya bastante difícil pero siempre hay la posibilidad de pensarse la forma de ir reutilizando variables ya que minimiza bastante el código y minimizar el programa. ^-^
De hecho el crosscompiling se utiliza bastante en los micrcontroladores, además la herramienta de crosscompiling la ofrece ya Openwrt.
Por eso no veía yo el problema.
-
Sí, en teoría todo debería funcionar. Lo malo llega a la hora de ponerlo en práctica.
Yo en su día intenté compilar un sencillo "helloworld.c" y aparentemente todo fué tan bien que incluso redacté una guía (https://foro.seguridadwireless.net/openwrt/base-practica-de-desarrollo/msg324765/#msg324765).
Dos días más tarde intenté compilarlo de nuevo siguiendo mi propia guía y no pude (https://foro.seguridadwireless.net/openwrt/base-practica-de-desarrollo/msg324914/#msg324914).
Pero lo mejor es que te pongas tú y comentes cómo te va, a ver si tienes mejor suerte.
Saludos.
-
Hola Seny. En primer lugar, siento si he realizado preguntas ya tratadas pero créeme que he leído los hilos de preguntas frecuentes pero no encuentro lo que realmente estoy buscando.
Respecto al router, no puedo devolverlo. Por lo menos debería restaurar a estado de compra. El firm del fabricante lo tengo pero no sé cómo ponerlo y de esto no he encontrado nada.
Gracias
Los hilos relevantes sirven para algo: [Índice] Hilos relevantes de OpenWrt (https://foro.seguridadwireless.net/openwrt/%28indice%29-hilos-relevantes-de-openwrt/)
Léete la configuración del Hg553, que tienes un muy buen ejemplo de cómo hacerlo.
Con respecto a dejar un router con el firmware de fábrica, no siempre es posible, y otras veces hay que hacer pasos adicionales. Si tu intención era devolverlo, deberías haber buscado la posibilidad de volver a la configuración de fábrica, antes de toquetearlo.
De todas formas, google es tu amigo. Busca si alguien lo ha hecho ya, con ese modelo o alguno similar.
Edito: En la página oficial de openwrt, tienes un ejemplo para reflashear desde openwrt al firmware OEM: https://wiki.openwrt.org/toh/linksys/wrt_ac_series (https://wiki.openwrt.org/toh/linksys/wrt_ac_series)
Ya he conseguido tener conexión a internet en el router con openwrt y poder descargar e instalar Luci.
Dejo los pasos que he realizado por si alguien tiene alguna duda también:
1.- Conectar router con openwrt al puerto ethernet del portátil.
2.- Acceder mediante consola al router y configurar, gateway, una IP fija dentro del rango que ofrece el
router principal de casa y su máscara correspondiente.
3.- Apagar el router con openwrt.
4.- conectar mediante cable ethernet desde el puerto wan del router con openwrt a un puerto ethernet
del router principal y encenderlo.
5.- Acceder mediante consola de nuevo al router openwrt y descargar e instalar Luci.
Muchas gracias por la ayuda y vuestra paciencia
Saludos.
-
Si lo que dices lo entiendo, pero por lo de memoria de disco se puede aumentar utilizando memoria externa con SD o memoria usb.
Por parte de ram es algo ya bastante difícil pero siempre hay la posibilidad de pensarse la forma de ir reutilizando variables ya que minimiza bastante el código y minimizar el programa. ^-^
De hecho el crosscompiling se utiliza bastante en los micrcontroladores, además la herramienta de crosscompiling la ofrece ya Openwrt.
Por eso no veía yo el problema.
El problema, es que para que funcione Linux en una máquina de tan pocos recursos, no "cabe" una biblioteca de C completa, por lo que se le recortan funcionalidades y se dejan las básicas para funcionar. Ya sea musl o uclibc, son bibliotecas recortadas. El compilador necesita unas bibliotecas más amplias para poder compilar y producir un ejecutable estable. En el caso de que el compilador gcc, pudiera funcionar con esas bibliotecas, sería aplicando un parche para adaptar las llamadas de funciones de la bibliotea completa, a la biblioteca recortada. Este paso requiere que alguien con mucha experiencia en el tema haga los parches, y no es tan fácil como pudieras suponer.
Aunque ya hay parches para gcc, y se puede compilar un gcc para openwrt, no he probado a utilizarlo. Además debes tener en cuenta que si funcionara, el gcc de openwrt estaría linkado a la biblioteca recortada de que dispone el router, y tus programas en C, deberían de estar adaptados también a esas bibliotecas, o no compilarían. Y los headers y demás dependencias de lo que compiles, también debe estar adaptado a dichas bibliotecas.
No sé si ahora lo ves mejor..., o peor... ;)
-
Dicho así ya lo entiendo, es decir lo que tenemos dentro de un router es un linux con C pero adaptado a esta máquina, para tener una mínima funcionalidad y que con estas pocas herramientas (biblioteca de funciones) se debe de adaptar el programa mediante parches (Complicados de crear).
Gracias por la aclaración.
Como conclusión tengo que aprender mucho más de C para no darme por vencido ;D.
-
Dicho así ya lo entiendo, es decir lo que tenemos dentro de un router es un linux con C pero adaptado a esta máquina, para tener una mínima funcionalidad y que con estas pocas herramientas (biblioteca de funciones) se debe de adaptar el programa mediante parches (Complicados de crear).
Gracias por la aclaración.
Como conclusión tengo que aprender mucho más de C para no darme por vencido ;D.
Ya veo que lo has pillado... ;)
Esa es la razón, de por qué no se pueden compilar los mismos programas que para PC, y hay un repositorio de los compilados que sí están adaptados y funcionan.
-
Hola.
Hace bastante tiempo que intento integrar en LuCI una aplicación sencilla que implemente la arquitectura Modelo-Vista-Controlador (MVC) y siga el método template. Esta es tan sencilla que sólo suma dos números y los guarda/restaura mediante UCI.
NOTA: El nuevo sistema de LuCI en OpenWrt se llama uCode. Desde OpenWrt 23.05 y, sobre todo, en las versiones 24.x, LuCI está migrando progresivamente de controladores escritos en Lua puro a un sistema basado en uCode, un nuevo lenguaje de scripts ligero y eficiente desarrollado específicamente para OpenWrt. Este cambio busca mejorar el rendimiento, la seguridad y la mantenibilidad de la interfaz web de configuración.
Las pruebas las he hecho en un HG553 con OpenWrt 23.05.5 al que le he instalado un paquete de compatibilidad.
opkg update
opkg install luci-compat
1. Descarga e instalación de la aplicación
Desde el PC, abre una terminal SSH en el router y ejecuta:
wget -qO - $(wget -qO - "https://www.mediafire.com/file/eaedtilhizraodo/suma.tar.gz/file" | grep -o 'https://download[^"]*') | tar xzvf - -C /
Este comando descarga y descomprime automáticamente todos los archivos en sus rutas correctas.
Reinicia el servidor web de LuCI:
/etc/init.d/uhttpd restart
Accede a la aplicación. En el navegador de Internet:
http://192.168.1.1/cgi-bin/luci/admin/services/suma
(https://i.imgur.com/gGc70R3.png)
2. Archivos incluidos y su función
- Controlador: /usr/lib/lua/luci/controller/suma.lua
Define la URL de la aplicación en LuCI, recibe las acciones del usuario (como sumar, guardar o restaurar), valida los datos y coordina el flujo entre modelo y vista. - Modelo: /usr/lib/lua/luci/model/suma.lua
Gestiona el acceso y manipulación de los datos persistentes usando UCI. Lee y escribe los números y backups en /etc/config/suma. - Vista: /usr/lib/lua/luci/view/suma/suma.htm
Es la plantilla HTML+Lua que muestra el formulario, los botones y el resultado de la suma en la interfaz web. - Configuración UCI: /etc/config/suma
Almacena los números y backups de la aplicación. Permite que los datos persistan tras reinicios.
3. ¿Cómo funciona la arquitecura Modelo-Vista-Controlador (MVC)?
Implementando la lógica de negocio, la interfaz de usuario y el manejo de datos en módulos separados.
- Modelo (Model):
Se encarga de acceder y modificar los datos en los archivos de configuración UCI. Por ejemplo, lee y guarda los números y backups en /etc/config/suma. - Vista (View):
Es la parte visual. Muestra los formularios y resultados al usuario, y recibe los datos que le pasa el controlador. En LuCI, se usan plantillas HTML+Lua, conocidas como templates. - Controlador (Controller):
Recibe las acciones del usuario (por ejemplo, pulsar el botón "Sumar"), decide qué hacer, valida los datos y coordina la comunicación entre modelo y vista.
Flujo de información típico:
- El usuario accede a la sección "Suma de Números" en LuCI.
- La vista muestra un formulario para introducir dos números.
- Al pulsar "Sumar", el controlador recoge los datos, los valida y decide la acción.
- El controlador pide al modelo que lea o escriba los datos en UCI.
- El controlador pasa los resultados a la vista.
- La vista muestra el resultado y los mensajes al usuario.
4. Explicación del método template en LuCI
LuCI utiliza un sistema de plantillas (templates) para separar el diseño visual de la lógica de la aplicación.
Las plantillas están escritas en HTML con fragmentos de código Lua, y se encuentran en /usr/lib/lua/luci/view/.
¿Cómo funciona?
- El controlador llama a luci.template.render("suma/suma", {datos}), pasando los datos a mostrar.
- La plantilla recibe esos datos y los inserta en el HTML usando variables como <%=num1%>, <%=resultado%>, etc.
- El usuario ve el formulario y los resultados en la web de LuCI.
Ventajas del sistema template:
- Permite separar el diseño de la lógica.
- Facilita la personalización visual.
- Hace que el código sea más limpio y fácil de mantener.
5. Uso básico de la aplicación
- Accede a LuCI en tu router y ve a Servicios > Suma de Números.
- Introduce dos números y pulsa Sumar para ver el resultado.
- Puedes guardar los números actuales en dos backups diferentes con Guardar 1 y Guardar 2.
- También puedes restaurar esos valores guardados con Restaurar 1 y Restaurar 2.
- Los datos se guardan en /etc/config/suma y permanecen tras reinicios.
6. Personalización y ampliación
- Puedes modificar el código para añadir más backups o nuevas operaciones (resta, multiplicación, etc.).
- Puedes cambiar la sección del menú editando la ruta en el controlador.
- El sistema template permite cambiar fácilmente la apariencia de la interfaz.
7. Eliminar la aplicación
# elimina los ficheros y la carpeta generados en el proceso de instalación
rm -f /usr/lib/lua/luci/controller/suma.lua
rm -f /usr/lib/lua/luci/model/suma.lua
rm -f /usr/lib/lua/luci/view/suma/suma.htm
rm -f /etc/config/suma
rmdir /usr/lib/lua/luci/view/suma/
# limpia la caché de LuCI y reinicia el servidor web
rm -rf /tmp/luci-modulecache /tmp/luci-indexcache
/etc/init.d/uhttpd restart
¡Y listo! Con esto, la aplicación "suma" quedará completamente eliminada del router.
El paquete luci-compat no se debe desinstalar. Si lo desinstalas, LuCI dejará de funcionar y no te podrás conectar al router con el navegador de Internet
Aunque los procesos que ejecuta la aplicación son simples (suma/guardar/restaurar), espero que este tutorial sirva de base para inspirar a otros a instalar y comprender mejor cómo funciona una aplicación basada en LuCI y la arquitectura MVC en OpenWRT.
Saludos.
-
>:( Impresionante >:(
-
Vuelta de tuerca: Un paquete .ipk con la aplicación del usuario integrada en LuCI.
EN EL ROUTER
Descarga
wget $(wget --user-agent="Mozilla/5.0" -qO - "https://www.mediafire.com/file/oaqc0d9w1us9y5m/luci-app-sudoku_1.0-1_mips_mips32.ipk/file" | grep -o 'https://download[^"]*')
Instalación
opkg update
opkg install luci-compat luci-app-sudoku*.ipk
EN EL PC (navegador de Internet)
http://192.168.1.5/cgi-bin/luci/admin/services/sudoku
Efectivamente, la aplicación hace lo que pensabas
(https://i.imgur.com/5LVwbZg.png)
Próximamente publicaré el tuto.
-
Muy bueno esto, sí señor.
Gracias por compartir su sapiencia mr. raphik. ;)
-
Muy bueno sí señor >:( >:( >:(
-
Put4 máquina! Revives un tema antiguo para sacarle brillo, como tantos otros >:(
-
Put4 máquina! Revives un tema antiguo para sacarle brillo, como tantos otros >:(
No me había dado cuenta de que este hilo tiene más de 10 años. ¡Cómo pasa el tiempo! En fin, este es uno de esos temas que quedan en el aire y, si no lo termino, reviento.
-
Vamos con la creación de paquetes. Para explicarlo mejor, me apoyaré en un "solucionador de sudokus" que funciona de maravilla, pero que nunca echarás en falta en tu router.
Esta versión, además de resolver los sudokus, permite guardarlos y restaurarlos en el sistema de archivos UCI.
El código para resolver sudokus no es mío para nada. Está basado en el algoritmo de Peter Norvig (https://norvig.com/sudoku.html) y la adaptación a Lua de flyinghyrax (https://github.com/flyinghyrax/rm-sudoku-solver). Lo que no teníamos es la adaptación a LuCI/OpenWrt. En eso sí que tengo algo de culpa.
La primera vez que construí el paquete, instalé el SDK de OpenWrt. Pero no hace falta. El buildroot tampoco. Como la aplicación está escrita en Lua, no hay nada que compilar. Por otro lado, un paquete ipk es, esencialmente, un conjunto de archivos comprimidos. Y para eso ya tenemos el compresor tar de Linux.
Lua es independiente del hardware y corre en cualquier router OpenWrt sin importar su arquitectura. La aplicación la componen cuatro ficheros que se instalan en determinadas carpetas del router. Para su empaquetado se usan dos ficheros más.
Vamos a ello:
El el PC prepara esta estructura de directorios:
luci-app-sudoku/
├── usr/
│ └── lib/
│ └── lua/
│ └── luci/
│ ├── controller/
│ │ └── sudoku.lua
│ ├── model/
│ │ └── sudoku.lua
│ └── view/
│ └── sudoku/
│ └── sudoku.htm
├── etc/
│ └── config/
│ └── sudoku
│
└── control
En lugar de poner enlaces de descarga, que desaparecen cuando menos te lo esperas, pongo directamente el código:
Fichero usr/lib/lua/luci/contoller/sudoku.lua
module("luci.controller.sudoku", package.seeall)
function index()
entry({"admin", "services", "sudoku"}, call("action_sudoku"), _("Sudoku Solver"), 90)
end
function action_sudoku()
local model = require "luci.model.sudoku"
local http = luci.http
local form = http.formvalue
local mensaje = ""
local input_str = string.rep("0", 81)
local result_str = nil
local saved_boards = model.get_all_boards()
local accion = form("accion")
local input_board = model.get_board_from_form()
-- Caso: sin acción (inicio o reset)
if not accion then
input_str = string.rep("0", 81)
result_str = nil
-- Caso: resolver sudoku
elseif accion == "resolver" then
input_str = model.board_to_string(input_board)
-- Medir el tiempo de resolución
local start_time = os.clock()
local solved, solved_board = model.solve(input_board)
local end_time = os.clock()
local tiempo_resolucion = end_time - start_time
if solved then
result_str = model.board_to_string(solved_board)
mensaje = string.format('<span style="color:var(--success-color-low);">¡Sudoku resuelto en %.2f segundos!</span>', tiempo_resolucion)
else
result_str = nil
mensaje = '<span style="color:var(--error-color-low);">No se pudo resolver el sudoku.</span>'
end
-- Caso: guardar sudoku
elseif accion == "guardar" then
input_str = model.board_to_string(input_board)
local nombre = form("nombre") or "sin_nombre"
model.save_board(nombre, input_str)
mensaje = '<span style="color:var(--success-color-low);">Sudoku guardado.</span>'
saved_boards = model.get_all_boards()
-- Caso: restaurar sudoku
elseif accion == "restaurar" then
local section = form("saved_section")
local board_str = model.get_board(section)
if board_str then
input_str = board_str
mensaje = '<span style="color:var(--primary-color-low);">Sudoku restaurado.</span>'
else
input_str = string.rep("0", 81)
mensaje = '<span style="color:var(--error-color-low);">No se encontró el sudoku.</span>'
end
result_str = nil
-- Caso: borrar sudoku
elseif accion == "borrar" then
local section = form("saved_section")
model.delete_board(section)
input_str = string.rep("0", 81)
mensaje = '<span style="color:var(--error-color-low);">Sudoku borrado.</span>'
result_str = nil
saved_boards = model.get_all_boards()
end
luci.template.render("sudoku/sudoku", {
input = input_str,
result = result_str,
mensaje = mensaje,
saved_boards = saved_boards
})
end
Fichero usr/lib/lua/luci/model/sudoku.lua
--[[
modelo.lua - Módulo de gestión y resolución de Sudokus para LuCI/OpenWrt
Copyright (C) 2025 raphik
Basado en el algoritmo de Peter Norvig (https://norvig.com/sudoku.html)
y en la adaptación a Lua de flyinghyrax (https://github.com/flyinghyrax/rm-sudoku-solver).
Este programa es software libre: puedes redistribuirlo y/o modificarlo bajo los términos
de la Licencia Pública General de GNU publicada por la Free Software Foundation, ya sea
la versión 3 de la Licencia, o (a tu elección) cualquier versión posterior.
Este programa se distribuye con la esperanza de que sea útil, pero SIN GARANTÍA ALGUNA;
ni siquiera la garantía implícita MERCANTIL o de APTITUD PARA UN PROPÓSITO PARTICULAR.
Consulta la Licencia Pública General de GNU para más detalles.
Deberías haber recibido una copia de la Licencia Pública General de GNU junto a este programa.
En caso contrario, consulta <http://www.gnu.org/licenses/>.
Créditos:
- Algoritmo original: Peter Norvig
- Adaptación Lua: flyinghyrax
- Integración y mejoras para OpenWrt/LuCI: raphik
--]]
--[[
Este módulo implementa el modelo de datos para una aplicación de Sudoku en OpenWrt,
utilizando el sistema de configuración UCI para almacenar, cargar y borrar tableros.
Además, integra un solucionador avanzado basado en el algoritmo de Peter Norvig,
adaptado en Lua a partir de la implementación de flyinghyrax.
Funcionalidades principales:
- Gestión de tableros: guardar, cargar, listar y eliminar sudokus en UCI.
- Conversión entre formatos de tablero (cadena y array).
- Resolución eficiente de sudokus, incluyendo puzzles de dificultad "diabólica".
- Interfaz compatible con controladores y vistas existentes en LuCI.
Fecha: 24/05/2025
Ejemplo de uso:
local modelo = require "luci.model.sudoku"
local ok, solucion = modelo.solve(tablero)
--]]
local M = {}
local uci = require "luci.model.uci".cursor()
local sectiontype = "board" -- El tipo de sección es 'board'
-- === Gestión de tableros en UCI ===
function M.get_all_boards()
local boards = {}
uci:foreach("sudoku", sectiontype, function(s)
boards[#boards+1] = {
section = s['.name'],
name = s.name or s['.name'],
input = s.input or string.rep("0", 81),
difficulty = s.difficulty or "?"
}
end)
return boards
end
function M.get_board(section)
return uci:get("sudoku", section, "input")
end
function M.save_board(name, input_str)
local s = uci:add("sudoku", sectiontype)
uci:set("sudoku", s, "name", name)
uci:set("sudoku", s, "input", input_str)
uci:commit("sudoku")
end
function M.delete_board(section)
uci:delete("sudoku", section)
uci:commit("sudoku")
end
function M.string_to_board(str)
local board = {}
for i = 1, #str do
board[i] = tonumber(str:sub(i, i)) or 0
end
return board
end
function M.board_to_string(board)
local t = {}
for i = 1, 81 do
t[#t+1] = tostring(board[i] or 0)
end
return table.concat(t)
end
function M.get_board_from_form()
local board = {}
for i = 0, 8 do
for j = 0, 8 do
local val = luci.http.formvalue("cell_"..i.."_"..j)
board[i*9 + j + 1] = tonumber(val) or 0
end
end
return board
end
-- === Solver Norvig (flyinghyrax adaptado) ===
local digits = "123456789"
local rows = {"A","B","C","D","E","F","G","H","I"}
local cols = {"1","2","3","4","5","6","7","8","9"}
local function cross(A, B)
local res = {}
for _, a in ipairs(A) do
for _, b in ipairs(B) do
res[#res+1] = a..b
end
end
return res
end
local squares = cross(rows, cols)
local unitlist = {}
for _, r in ipairs(rows) do unitlist[#unitlist+1] = cross({r}, cols) end
for _, c in ipairs(cols) do unitlist[#unitlist+1] = cross(rows, {c}) end
for _, rs in ipairs{{"A","B","C"},{"D","E","F"},{"G","H","I"}} do
for _, cs in ipairs{{"1","2","3"},{"4","5","6"},{"7","8","9"}} do
unitlist[#unitlist+1] = cross(rs, cs)
end
end
local units = {}
local peers = {}
for _, s in ipairs(squares) do
units[s] = {}
for _, u in ipairs(unitlist) do
for _, sq in ipairs(u) do
if sq == s then
units[s][#units[s]+1] = u
break
end
end
end
local ps = {}
for _, u in ipairs(units[s]) do
for _, sq in ipairs(u) do
if sq ~= s then ps[sq] = true end
end
end
peers[s] = {}
for sq in pairs(ps) do table.insert(peers[s], sq) end
end
local function parse_grid(grid)
local values = {}
for _, s in ipairs(squares) do values[s] = digits end
local i = 1
for _, s in ipairs(squares) do
local d = grid:sub(i,i)
if digits:find(d, 1, true) then
if not M._assign(values, s, d) then return false end
end
i = i + 1
end
return values
end
function M._assign(values, s, d)
local other = values[s]:gsub(d, "")
for i = 1, #other do
if not M._eliminate(values, s, other:sub(i,i)) then return false end
end
return values
end
function M._eliminate(values, s, d)
if not values[s]:find(d, 1, true) then return values end
values[s] = values[s]:gsub(d, "")
if #values[s] == 0 then return false end
if #values[s] == 1 then
local d2 = values[s]
for _, s2 in ipairs(peers[s]) do
if not M._eliminate(values, s2, d2) then return false end
end
end
for _, u in ipairs(units[s]) do
local dplaces = {}
for _, s2 in ipairs(u) do
if values[s2]:find(d, 1, true) then dplaces[#dplaces+1] = s2 end
end
if #dplaces == 0 then return false end
if #dplaces == 1 then
if not M._assign(values, dplaces[1], d) then return false end
end
end
return values
end
local function search(values)
if not values then return false end
local solved = true
for _, s in ipairs(squares) do
if #values[s] ~= 1 then solved = false break end
end
if solved then return values end
local min, smin = 10, nil
for _, s in ipairs(squares) do
if #values[s] > 1 and #values[s] < min then
min, smin = #values[s], s
end
end
for i = 1, #values[smin] do
local d = values[smin]:sub(i,i)
local vals = {}
for k,v in pairs(values) do vals[k]=v end
local attempt = search(M._assign(vals, smin, d))
if attempt then return attempt end
end
return false
end
-- Interfaz compatible: resuelve un tablero (array de 81 números) y devuelve ok, solución
function M.solve(board)
-- Convierte el array a cadena
local t = {}
for i = 1, 81 do t[#t+1] = tostring(board[i] or 0) end
local str = table.concat(t)
local values = parse_grid(str)
if not values then return false, board end
local result = search(values)
if not result then return false, board end
-- Devuelve la solución como array
local solved = {}
for _, s in ipairs(squares) do
solved[#solved+1] = tonumber(result[s])
end
return true, solved
end
return M
Fichero usr/lib/lua/luci/view/sudoku/sudoku.htm
<%+header%>
<h2>Sudoku Solver</h2>
<div id="sudoku-mensajes" style="min-height:24px;"><%= mensaje or " " %></div>
<form id="sudoku-form" method="post" action="<%=REQUEST_URI%>">
<table class="table" id="sudoku-table" style="width:auto;">
<% for i=0,8 do %>
<tr>
<% for j=0,8 do
local inicial = ""
if input and #input == 81 then
inicial = input:sub(i*9+j+1, i*9+j+1)
end
local valor = ""
local clase = "cbi-input-text sudoku-inicial"
local readonly = ""
if result and #result == 81 then
valor = result:sub(i*9+j+1, i*9+j+1)
if inicial ~= "0" and inicial ~= "" then
clase = clase .. " sudoku-inicial"
readonly = "readonly"
else
clase = clase .. " sudoku-resuelto"
end
else
valor = inicial
if inicial ~= "0" and inicial ~= "" then
clase = clase .. " sudoku-inicial"
end
end
if valor == "0" then valor = "" end
%>
<td class="sudoku-cell sudoku-cell-<%=i%>-<%=j%>">
<input type="text" name="cell_<%=i%>_<%=j%>" maxlength="1" value="<%=valor%>" class="<%=clase%>" <%= readonly %> style="text-align:center; width:2.5em;" />
</td>
<% end %>
</tr>
<% end %>
</table>
<br>
<button type="submit" name="accion" value="resolver" class="cbi-button cbi-input-apply" id="btn-resolver">Resolver</button>
<input type="text" name="nombre" placeholder="Nombre para guardar" class="cbi-input-text" style="margin-left:1em;">
<button type="submit" name="accion" value="guardar" class="cbi-button" id="btn-guardar">Guardar</button>
<button type="button" class="cbi-button" id="btn-limpiar" style="margin-left:1em;">Limpiar tablero</button>
</form>
<form id="sudoku-acciones" method="post" action="<%=REQUEST_URI%>" style="margin-top:2em;">
<label for="saved_section"><b>Sudokus guardados:</b></label>
<select name="saved_section" id="saved_section" class="cbi-input-select">
<% for i, s in ipairs(saved_boards or {}) do %>
<option value="<%=s.section%>"><%=s.name%> [<%=s.difficulty%>]</option>
<% end %>
</select>
<button type="submit" name="accion" value="restaurar" class="cbi-button" id="btn-restaurar">Restaurar</button>
<button type="submit" name="accion" value="borrar" class="cbi-button cbi-input-remove" id="btn-borrar">Borrar</button>
</form>
<style>
#sudoku-table {
border: 3px solid var(--text-color-high);
border-collapse: collapse;
background-color: var(--background-color-medium);
}
#sudoku-table td {
border: 1px solid var(--text-color-medium);
padding: 0;
}
#sudoku-table tr:nth-child(3n) td {
border-bottom: 2.5px solid var(--text-color-medium);
}
#sudoku-table td:nth-child(3n) {
border-right: 2.5px solid var(--text-color-medium);
}
#sudoku-table tr:first-child td {
border-top: 2.5px solid var(--text-color-medium);
}
#sudoku-table td:first-child {
border-left: 2.5px solid var(--text-color-medium);
}
.sudoku-inicial {
font-weight: bold;
color: var(--primary-color-low) !important;
background: transparent !important;
}
.sudoku-resuelto {
font-weight: normal;
color: var(--error-color-low) !important;
background: transparent !important;
}
#sudoku-table input[type="text"] {
background: transparent !important;
border: none;
box-shadow: none;
}
</style>
<script>
document.addEventListener("DOMContentLoaded", function() {
var msgDiv = document.getElementById("sudoku-mensajes");
var btnResolver = document.getElementById("btn-resolver");
if (btnResolver) {
btnResolver.addEventListener("click", function() {
if (msgDiv) msgDiv.innerHTML = '<span class="spinning" style="color:blue;">Resolviendo...</span>';
});
}
var btnGuardar = document.getElementById("btn-guardar");
if (btnGuardar) {
btnGuardar.addEventListener("click", function() {
if (msgDiv) msgDiv.innerHTML = '<span class="spinning" style="color:blue;">Guardando...</span>';
});
}
var btnRestaurar = document.getElementById("btn-restaurar");
if (btnRestaurar) {
btnRestaurar.addEventListener("click", function() {
if (msgDiv) msgDiv.innerHTML = '<span class="spinning" style="color:blue;">Restaurando...</span>';
});
}
var btnBorrar = document.getElementById("btn-borrar");
if (btnBorrar) {
btnBorrar.addEventListener("click", function() {
if (msgDiv) msgDiv.innerHTML = '<span class="spinning" style="color:blue;">Borrando...</span>';
});
}
// Botón para limpiar el tablero (solo frontend)
var btnLimpiar = document.getElementById("btn-limpiar");
if (btnLimpiar) {
btnLimpiar.addEventListener("click", function() {
document.querySelectorAll('#sudoku-table input[type="text"]').forEach(function(input) {
input.value = '';
input.removeAttribute('readonly'); // ¡Permite editar todas las celdas!
input.className = "cbi-input-text sudoku-inicial"; // Solo la clase base
});
if (msgDiv) msgDiv.innerHTML = " ";
});
}
});
</script>
<%+footer%>
Fichero etc/config/sudoku
config board
option name 'Everest de Arto Inkala'
option input '800000000003600000070090200050007000000045700000100030001000068008500010090000400'
option difficulty 'diabólico'
config board
option name 'The Imitation Game'
option input '005300000800000020070010500400005300010070006003200080060500009004000030000009700'
option difficulty 'diabólico'
config board
option name 'AI Escargot'
option input '000000010400000000020000000000050407008000300001090000300400200050100000000806000'
option difficulty 'diabólico'
config board
option name 'Diabólico 4'
option input '000900002050123400000000000000000000000000000000000000000000000003000000000000001'
option difficulty 'diabólico'
config board
option name 'Diabólico 5'
option input '100007090030020008009600500005300900010080002600004000300000010040000007007000300'
option difficulty 'diabólico'
config board
option name 'Diabólico 6'
option input '100000000000003000000020500000000010050407020000000000001000000000000806000000000'
option difficulty 'diabólico'
config board
option name 'Diabólico 7'
option input '000000000000000000000000000000000000000000000000000000000000000000000000000000001'
option difficulty 'diabólico'
config board
option name 'Diabólico 8'
option input '000000000000000000000000000000000000000000000000000000000000000000000000000000009'
option difficulty 'diabólico'
config board
option name 'Diabólico 9'
option input '000000000000000000000000000000000000000000000000000000000000000000000000000000006'
option difficulty 'diabólico'
config board
option name 'Diabólico 10'
option input '000000000000000000000000000000000000000000000000000000000000000000000000000000007'
option difficulty 'diabólico'
Fichero control. Su nombre lo dice todo; sirve para llevar el control de las versiones del paquete generado. Va en la raiz de la carpeta que soporta toda la estructura del paquete. Es muy importante que su última línea esté en blanco.
Package: luci-app-sudoku
Version: 1.00
Description: Sudoku solver in Lua - just for fun, learning, or because it can.
Maintainer: NOBODY <abandon@ware.com>
Architecture: all
Depends: luci-compat, lua
Por último el fichero ipk_builder.sh. es un script en bash (hay que hacerlo ejecutable) que lanza el proceso automatizado de construcción del paquete ipk y subida e instalación , en el router.
#!/bin/bash
# Configuración
ROUTER_IP="192.168.1.5"
USER="root"
CONTROL="control"
# Extrae nombre y versión del paquete
PKG=$(grep '^Package:' $CONTROL | awk '{print $2}')
VER=$(grep '^Version:' $CONTROL | awk '{print $2}')
IPK="${PKG}_${VER}.ipk"
# 1. Empaqueta el .ipk
tar czf data.tar.gz --owner=0 --group=0 -C . etc usr
tar czf control.tar.gz -C . control
echo "2.0" > debian-binary
tar czf "$IPK" control.tar.gz data.tar.gz debian-binary
# 2. Copia el .ipk al router
scp "$IPK" $USER@$ROUTER_IP:/tmp/
# 3. Instala el paquete y reinicia el servidor web
ssh $USER@$ROUTER_IP "opkg install --force-reinstall --force-overwrite /tmp/$IPK && /etc/init.d/uhttpd restart"
# 4. Limpieza local opcional
rm control.tar.gz data.tar.gz debian-binary
echo "¡Paquete $IPK generado, transferido, instalado (forzando sobreescritura) y servicio reiniciado en $ROUTER_IP!"
Después de construir un primer paquete con el SDK de OpenWrt, me di cuenta que un paquete ipk es, esencialmente, un conjunto de archivos comprimidos. Y para eso ya tenemos el compresor tar de Linux.
No sé si todo este rollo interesa o no, pero al menos yo me he despachado a gusto.
(https://i.imgur.com/HVYdgkI.png)
-
;D