Mensajes recientes

Páginas: 1 2 3 4 5 6 7 [8] 9 10
71
Colaboracion y desarrollo de nuestras lives / Re:Script multiuso en bash, airgeddon
« Último mensaje por v1s1t0r en 30-05-2025, 13:54 (Viernes) »
Teneis un módulo de vmware tools para esta version de Wifislax? insertando el cd de vmware, se instala pero luego no funciona ni el clipboard ni el resize de resolución... tuve q echar para atrás el snapshot.

Las vmware tools las necesito como el comer, ya que para arrastrar las nuevas versiones de airgeddon desde mi host y probarlas, el portapapeles... me es de vital importancia para no tardar mucho en hacer cualquier cosa.

Puedo probar cosas sin miedo a romper, dado q tengo snapshot justo antes. ¿Sugerencias? thanks.

P.D. No he visto que haya módulo en el hilo https://foro.seguridadwireless.net/live-wifislax/remodulos-extra-para-wifislax64-4-0/
72
Colaboracion y desarrollo de nuestras lives / Re:Script multiuso en bash, airgeddon
« Último mensaje por USUARIONUEVO en 30-05-2025, 10:57 (Viernes) »
Me hago viejo , ya no valgo ni para copiar y pegar un enlace xD

pepe10000  estaba en lo cierto , el enlace por si alguien quiere el parche

https://upfion.com/PrxfhkT1

73
Colaboracion y desarrollo de nuestras lives / Re:Script multiuso en bash, airgeddon
« Último mensaje por pepe10000 en 30-05-2025, 10:45 (Viernes) »
Llego tarde, pero si no me equivoco el parche se puede descargar de aquí:

https://foro.seguridadwireless.net/live-wifislax/wifislax64-4-0-final-iso-para-descarga/msg376046/#msg376046

Un saludo.
74
Colaboracion y desarrollo de nuestras lives / Re:Script multiuso en bash, airgeddon
« Último mensaje por v1s1t0r en 30-05-2025, 10:19 (Viernes) »
Vale! ha funcionado... creé el disco duro en el VMWare como SATA en lugar del por defecto SCSI y haciendo esa modificación en el instalador de grub ya todo ha ido como la seda. Muchas gracias!!!  >:(
75
Colaboracion y desarrollo de nuestras lives / Re:Script multiuso en bash, airgeddon
« Último mensaje por v1s1t0r en 30-05-2025, 10:13 (Viernes) »
Hola USUARIONUEVO, gracias por contestar. El enlace que pasas pide usuario y contraseña. Parece que no es público. No sé si tendría que registrar una cuenta en upfiles, pero bueno, viendo que parece un error que os es conocido, decidí probar a editar el script manualmente.

Edité el /usr/bin/Grub2InstallerGUI y tal y como comentas, añadí el --target=i386-pc en el "else" del condicional:



Bueno, el primero solo es un echo, la línea del grub-install es la importante. El caso es que tras esta modificación, el grub ya cuando lo lanzas se instala bien (o eso dice):



Pero luego al reiniciar, me da un error kernel panic:



Voy a probar un par de configuraciones más de vmware modificando el script a ver si suena la flauta... probaré con disco SATA y alguna cosa más, que no sé si puede estar afectando. Yo de momento uso el "por defecto SCSI". Si consigo hacerlo funcionar aviso. Y si no, ¿alguna otra sugerencia? gracias
76
Colaboracion y desarrollo de nuestras lives / Re:Script multiuso en bash, airgeddon
« Último mensaje por USUARIONUEVO en 30-05-2025, 02:47 (Viernes) »
Publique un patch que corrige la instalacion en maquina virtual ...ahora hay que especificar la plataforma x86 , antes lo hacia solo pero algo cambio en los nuevos paquetes de grub.

Te dejo el parche , ponlo en modules...basicamente cuando no hay efi , hay que decirle a grub que instale la version x86

--target=i386-pc

PARCHE:
https://upfiles.com/member/statistics/file/1751744

Si lo prefieres abre el script de instalacion de grub , veras la opcion por defecto EFI , y si no hay efi ..otra orden de grub-install un poco mas basica ...añadele simplemente el target que he puesto y ya te dejara instalar el grub.

Solo necesitas instalar el grub , no hace falta reinstalarlo todo.  ;)

77
Colaboracion y desarrollo de nuestras lives / Re:Script multiuso en bash, airgeddon
« Último mensaje por v1s1t0r en 29-05-2025, 17:13 (Jueves) »
Hola, estoy cerca de sacar la release v11.50 de airgeddon, pero quería probarla bien con la última iso de wifislax "wifislax64-4.0-final.iso". Hacía tiempo que no instalaba una vm con wifislax y me he encontrado un problema inesperado. Uso vmware, como siempre, y nunca había tenido problemas, así que no sé si será algo de la última iso. No quiero desviar mucho el tema del thread ya que es sobre airgeddon, pero bueno, de alguna manera, necesito instalar un wifislax para poder probar bien airgeddon antes de sacar la release. Mi problema es el siguiente:

Cuando arranco la iso en modo live y le doy a instalar, me pide que abra gparted (eso como siempre), y que cree la tabla de particiones (eso también como siempre, que se creaba como tipo msdos). Ok, luego hay que crear la/s partición/es. Estoy un poco desactualizado, pero juraría que ya no era necesario 100% crear una linux-swap, así que creo una partición que ocupe todo el disco de 20gb que asigné (partición primaria, ext4, opciones por defecto). Escribo los cambios y ok. Luego empieza la instalación de wifislax y ok, pero cuando llega a la instalación de grub, da un error muy genérico que no me dice nada.



Seguramente estoy haciendo algo mal, a ver si me podeis ayudar. He mirado un par de videos en youtube y no veo que haya que hacer nada especial, yo hago lo mismo pero a mi no me funciona. Es extraño...

Os cuento otras cosas que he intentado:

-He probado a asignar disco más grande o más pequeño. Mismo resultado.
-He probado en el vmware para que el disco en lugar de SCSI que es el default, sea SATA. Mismo resultado.
-He probado a arrancar diferentes entornos gráficos en la opción live, KDE, XFCE, etc. Mismo resultado.
-He probado a elegir la opción "MBR Se instalará en /dev/sda (Recomendado)", También a de "ROOT Se instalará en /dev/sda1 (Experto)". Mismo resultado.
-He probado a crear una partición de tipo Linux-Swap de un par de gigas aparte de la partición normal. Mismo resultado.
-He probado a borrar la vm entera y crearla de nuevo varias veces por si acaso... Mismo resultado.

Indagando un poco, lanzando el instalador desde consola, muestra este error:



Espero que este último pantallazo con el error sea más esclarecedor. Y sí, ya sé que no hace falta sudo que ya soy root... me di cuenta después  ^-^

No recordaba que instalar un Wifislax fuera difícil, así que o bien estoy omitiendo algo muy básico que no recuerdo, o esta iso tiene algo distinto. ¿Un empujoncito please? gracias.

P.D. Sí, también sé que para probar, con una live me vale... pero quería dejar instalada una vm con Wifislax si era posible.
78
Openwrt & LEDE / [Tutorial] ¿Qué es un paquete .ipk?
« Último mensaje por raphik en 28-05-2025, 14:11 (Miércoles) »
Un paquete .ipk es un archivo comprimido que contiene uno o varios archivos —asociados a una ruta de destino— listos para ser instalados en dispositivos que ejecutan el S.O. OpenWrt.

OpenWrt usa paquetes .ipk y el gestor opkg para instalar, actualizar y eliminar software de forma modular, pero su flexibilidad permite también automatizar configuraciones, desplegar servicios, distribuir archivos personalizados y restaurar el sistema, adaptando el router a múltiples usos más allá de la simple gestión de aplicaciones

Por ejemplo, imaginemos que queremos cambiar el banner de OpenWrt. En lugar de modificar manualmente el archivo en el sistema, podemos crear un paquete .ipk que incluya el nuevo banner y, al instalarlo, el cambio se aplicará automáticamente de forma controlada y repetible.

EN EL PC

# 1. Abrir una terminal, generar la carpeta raíz del proyecto y la de ubicación del banner
Código: [Seleccionar]
mkdir -p ~/Documentos/nuevo-banner/etc

# 2. Generar el fichero banner (copia y pega en la terminal)
Código: [Seleccionar]
cat << 'EOF' > ~/Documentos/nuevo-banner/etc/banner
 _________________________
/ Just for fun, learning, \
\  or because it can.     /
 -------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
EOF

# 3. Colocarse en la raiz del proyecto
Código: [Seleccionar]
cd ~/Documentos/nuevo-banner

# 4. Generar el fichero contol (copia y pega en la terminal)
Código: [Seleccionar]
cat << 'EOF' > ~/Documentos/nuevo-banner/control
Package: nuevo-banner
Version: 1.00
Description: Cambio de banner - Just for fun, learning, or because it can.
Maintainer: NOBODY <abandon@ware.com>
Architecture: all

EOF

La estructura del paquete es esta:


# 5. Generar el archivo data.tar.gz con el archivo que se instalará en el router.
Código: [Seleccionar]
tar czf data.tar.gz --owner=0 --group=0 -C . etc

# 6. Generar el archivo control.tar.gz (metadatos del paquete).
Código: [Seleccionar]
tar czf control.tar.gz -C . control

# 7. Generar el archivo debian-binary (texto 2.0)
Código: [Seleccionar]
echo "2.0" > debian-binary

# 8. Generar el paquete .ipk (contiene los ficheros: data-tar-gz + control.tar.gz + debian-binary)
Código: [Seleccionar]
tar czf nuevo-banner_1.0.ipk control.tar.gz data.tar.gz debian-binary


# 9. Copiar el paquete en la carpeta /tmp del router
Código: [Seleccionar]
scp nuevo-banner_1.0.ipk@192.168.1.1:/tmp/

# 10. Instalar el paquete en el router
ssh root@192.168.1.1 "opkg install --force-overwrite /tmp/nuevo-banner_1.0.ipk"


EN EL ROUTER

# 1. Al inicar sesión, se visualiza el nuevo banner


# 2. Eliminar el paquete
Código: [Seleccionar]
opkg remove nuevo-bannerNOTA: Al eliminar el paquete, no se restaura el antiguo banner. Para restaurarlo, hay que guardarlo antes de instalar el paquete.


SCRIPTS OPCIONALES

Son archivos que pueden forman parte del ciclo de vida de los paquetes .ipk. Se ejecutan antes o después de la instalación o eliminación del paquete.
┌──────────┬───────────────────────────────────────┐
Script    Momento de ejecución                 
├──────────┼───────────────────────────────────────┤
preinst  Antes de instalar el paquete         
├──────────┼───────────────────────────────────────┤
postinst Después de instalar el paquete       
├──────────┼───────────────────────────────────────┤
prerm    Antes de eliminar el paquete         
├──────────┼───────────────────────────────────────┤
postrm    Después de eliminar el paquete       
└──────────┴───────────────────────────────────────┘

Todos estos scripts se colocan en el archivo control.tar.gz del paquete y no llevan extensión.
Si no se incluye un script, simplemente no se ejecuta esa acción durante el ciclo de vida del paquete.


Ejemplo: Se puede preservar el banner original incluyendo un par de ficheros opcionales en la carpeta raíz del paquete:

Script preinst (ejecución automática previa a la instalación)
Código: [Seleccionar]
mv /etc/banner /etc/banner_old
Script postrm (ejecución automática posterior a la desisntalación)
Código: [Seleccionar]
mv /etc/banner_old /etc/banner

ARCHIVOS PROTEGIDOS

El archivo conffiles también es opcional. Contiene las rutas absolutas de aquellos archivos que se deben preservar durante los procesos de actualización o reinstalación. De este modo mantienen su contenido.

Ejemplo: Si se realiza una reinstalación de nuestro paquete ejemplo, el contenido original de banner_old se perdería. Para evitarlo, basta incluirlo en el archivo conffiles.

Fichero conffiles
Código: [Seleccionar]
/etc/banner_old

Saludos.
79
Openwrt & LEDE / Re:Base práctica de desarrollo
« Último mensaje por Hwagm en 27-05-2025, 20:29 (Martes) »
 ;D
80
Openwrt & LEDE / Re:Base práctica de desarrollo
« Último mensaje por raphik en 27-05-2025, 19:18 (Martes) »
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
Código: [Seleccionar]
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
Código: [Seleccionar]
--[[
    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
Código: [Seleccionar]
<%+header%>
<h2>Sudoku Solver</h2>
<div id="sudoku-mensajes" style="min-height:24px;"><%= mensaje or "&nbsp;" %></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 = "&nbsp;";
    });
  }
});
</script>
<%+footer%>

Fichero etc/config/sudoku
Código: [Seleccionar]
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.
Código: [Seleccionar]
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.
Código: [Seleccionar]
#!/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.

Páginas: 1 2 3 4 5 6 7 [8] 9 10