From 39e82c2bde3e5786ccf5cf1c0442dbc80b18b951 Mon Sep 17 00:00:00 2001 From: Stefano Babic Date: Wed, 4 Jul 2018 16:08:10 +0200 Subject: [PATCH] SWupdateGUI: simple GUI for SWUpdate's rescue Signed-off-by: Stefano Babic --- README.md | 2 +- SWUpdate.svg | 88 ---- SWUpdateGUI.lua | 691 +++++++++++++++++++++++++ SWUpdateGUI_ipaddress.lua | 145 ++++++ SWUpdateGUI_osinterface.lua | 24 + common.lua | 38 ++ config.lua | 9 + logo.png | Bin 0 -> 11983 bytes tek/ui/locale/SWUpdate/SWUpdate-GUI/de | 11 + 9 files changed, 919 insertions(+), 89 deletions(-) delete mode 100644 SWUpdate.svg create mode 100755 SWUpdateGUI.lua create mode 100755 SWUpdateGUI_ipaddress.lua create mode 100644 SWUpdateGUI_osinterface.lua create mode 100644 common.lua create mode 100644 config.lua create mode 100644 logo.png create mode 100644 tek/ui/locale/SWUpdate/SWUpdate-GUI/de diff --git a/README.md b/README.md index 4d274f4..41526ea 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -

+

RescueGUI - a simple GUI for SWUpdate in rescue mode ==================================================== diff --git a/SWUpdate.svg b/SWUpdate.svg deleted file mode 100644 index 6381d38..0000000 --- a/SWUpdate.svg +++ /dev/null @@ -1,88 +0,0 @@ - - - SWUpdate Logo - - - - image/svg+xml - - SWUpdate Logo - - - - - - - - - Andres Babic <andres.babic@gmail.com> - - - - - - - - - - - - - - - - - - - - diff --git a/SWUpdateGUI.lua b/SWUpdateGUI.lua new file mode 100755 index 0000000..ca46d57 --- /dev/null +++ b/SWUpdateGUI.lua @@ -0,0 +1,691 @@ +#!/usr/bin/env lua + +-- GUI for SWUPdate Rescue mode +-- +-- (C) Copyright 2018 +-- Stefano Babic, DENX Software Engineering, sbabic@denx.de. +-- +-- SPDX-License-Identifier: GPL-2.0-or-later +-- +-- This is a simple GUI for the SWUpdate tool +-- and it is thought to be used when SWUpdate runs +-- as rescue system. +-- + +-- check for required not standard modules +-- lfs is required to browse files on local media + +VERSION="0.1" + +STATUS_IDLE = 0 +STATUS_START = 1 +STATUS_RUN = 2 +STATUS_SUCCESS = 3 +STATUS_FAILURE = 4 +STATUS_DONE = 6 + +-- interface name for default Gateway +INTF_GATEWAY = "DefaultGateway" + +-- Check for components outside this project +-- they must be available, else an error is reported +local lfs +pcall(function() lfs = require "lfs" end) +if not lfs then + print "This program requires the luafilesystem library." + return +end +local sw = require "lua_swupdate" +if not sw then + print "This program requires the lua swupdate library." + return +end + +-- Load other modules of the project +require "common" +require "SWUpdateGUI_ipaddress" +require "SWUpdateGUI_osinterface" + +-- This is the interface to the tekui library +-- Load modules that are used in this application +local ui = require "tek.ui" +local List = require "tek.class.list" +local Button = ui.Button +local ImageWidget = ui.ImageWidget +local Group = ui.Group +local Text = ui.Text +local exec = require "tek.lib.exec" +local Gauge = ui.Gauge +local db = require "tek.lib.debug" +local rdargs = require "tek.lib.args".read + +-- Set default values, they can be overwritten +-- in the config.lua file +APP_ID = "SWUpdate-GUI" +VENDOR = "SWUpdate" +LOGO = "images/logo.png" +LANG = "en" + +-- TODO: is it necessary or better use just config.lua ? +local ARGTEMPLATE = "-r=rotate/N,--help=HELP/S,-l=LOCALE/S" +local args = rdargs(ARGTEMPLATE, arg) +if not args or args.help then + print(ARGTEMPLATE) + return +end + +function lfs.readdir(path) + local dir, iter = lfs.dir(path) + return function() + local e + repeat + e = dir(iter) + until e ~= "." and e ~= ".." + return e + end +end + +-- Try to load configuration file +-- if any +local config +pcall(function() config = require "config" end) + +local L = ui.getLocale("SWUpdate-GUI", VENDOR, "en", LANG) +LogoImage = ui.loadImage(LOGO) + +interfaces = { } + +-- This is taken from the book +-- Programming LUA, 4nd edition +function pairsByKeys (t, f) + local a = {} + for n in pairs(t) do table.insert(a, n) end + table.sort(a, f) + local i = 0 -- iterator variable + local iter = function () -- iterator function + i = i + 1 + if a[i] == nil then return nil + else return a[i], t[a[i]] end + end + return iter +end + +------------------------------------------------------------------------------- +-- Find the local interface and add widgets to show the IP addresses +------------------------------------------------------------------------------- +-- +-- Getting default gateway + +function convertip(ip) + if not (#ip == 8) then return nil end + val = string.format("%d.%d.%d.%d", tonumber("0x" .. string.sub(ip,7,8)), + tonumber("0x" .. string.sub(ip,5,6)), + tonumber("0x" .. string.sub(ip,3,4)), + tonumber("0x" .. string.sub(ip,1,2))) + return val +end + +function getgatewayip() + local f = assert(io.open("/proc/net/route", "r")) + for entry in f:lines() do + local intf, dest, gw = string.match(entry, "(%g+)%s+(%g+)%s+(%g+)") + if (dest == "00000000") then + f:close() + return convertip(gw) + end + end + f:close() + return nil +end + +-- Update an entry in the interface table +function modinterface(name, dhcp, addr, netmask) + for _, t in pairs(interfaces) do + if t["name"] == name then + t["dhcp"] = dhcp + t["addr"] = addr + t ["netmask"] = netmask + end + end +end + +function updnetinterfaces() + local netgroup = app:getById("interfaces-group") + for _, t in pairs(interfaces) do + if not (t["name"] == INTF_GATEWAY) then + local netcaption = ui.Text:new + { + Id = "net-" .. t["name"], + Text = t["name"] .. ": " .. t["addr"] .. " " .. t["netmask"], + HAlign = "left", + Height = "auto", + Style = "font: ui-xx-large/bi; text-align:left" + } + elem = app:getById("net-" .. t["name"]) + if elem then + elem:setValue("Text", t["name"] .. ": " .. t["addr"] .. " " .. t["netmask"]) + else + netgroup:addMember(netcaption) + end + end + end +end + +function findintf(name) + for _, t in pairs(interfaces) do + if t["name"] == name then + return t + end + end + return nil +end + +function loadnetinterfaces() + local runintf = sw:ipv4() + local gw = getgatewayip() + + runintf[INTF_GATEWAY] = gw .. " 0.0.0.0" + + if NETWORK_INTERFACES then + for cnt=1, #NETWORK_INTERFACES do + local name = NETWORK_INTERFACES[cnt] + if not runintf[name] then + runintf[name] = "" + end + end + end + + for name,v in pairsByKeys (runintf) do + local intf = findintf(name) + local addr,netmask = string.match(v, "(.*) (.*)") + if not addr then addr = "" end + if not netmask then netmask = "" end + + if not intf then + t = {} + t["name"] = name + t["dhcp"] = false + t["addr"] = addr + t["netmask"] = netmask + table.insert(interfaces, t) + else + intf["addr"] = addr + intf["netmask"] = netmask + end + end + updnetinterfaces() +end + +------------------------------------------------------------------------------- +-- Progress Window +------------------------------------------------------------------------------- + +local progwin = ui.Window:new +{ + Id = "progress-window", + Title = APP_ID .. " " .. VERSION, + Orientation = "vertical", + Status = "hide", + HideOnEscape = true, + SizeButton = true, + interval = 0, + show = function(self) + ui.Window.show(self) + self.Window:addInputHandler(ui.MSG_KEYDOWN, self, self.keypressed) + self.Window:addInputHandler(ui.MSG_INTERVAL, self, self.tick) + self.interval = 0 + end, + hide = function(self) + ui.Window.hide(self) + self.Window:remInputHandler(ui.MSG_KEYDOWN, self, self.keypressed) + self.Window:remInputHandler(ui.MSG_INTERVAL, self, self.tick) + end, + keypressed = function(self) + self.Window:hide() + w = self:getById("MainWindow") + w:setValue("Status", "show") + end, + tick = function(self) + self.interval = self.interval + 1 + if ((self.interval % 50) == 0) then + print(self.interval) + end + end, + Children = + { + RescueGUIHeader:new + { + Description = APP_ID .. " " .. VERSION, + Image = LogoImage, + }, + ui.Text:new + { + Class = "caption", + Id = "progress-caption", + Style = "border-style:ridge; border-rim-width: 1; border-focus-width: 1; border-width: 4;", + Text = L.UPDATE_IN_PROGRESS, + }, + + Group:new + { + Orientation = "vertical", + Children = + { + Gauge:new + { + Legend = L.NUMBER_OF_STEPS, + Min = 0, + Max = 100, + Height = 200, + Id = "slider-steps", + Text = "Progress" + }, + Gauge:new + { + Legend = L.CURRENT_STEP, + Min = 0, + Max = 100, + Height = 200, + Id = "slider-step", + Text = "Progress1" + }, + + ui.ScrollGroup:new + { + Legend = L.OUTPUT, + VSliderMode = "auto", + Child = ui.Canvas:new + { + AutoWidth = true, + Child = ui.FloatText:new + { + Id = "message-box", + } + } + } + } + } + } +} + +------------------------------------------------------------------------------- +-- Network Window +------------------------------------------------------------------------------- + +local NetWindow = ui.Window:newClass { _NAME = "_network_window" } + +function NetWindow:setRecord(fields) + local net = self:getById("network-fields") + local dhcp = false + if fields[2] == "yes" then + dhcp = true + end + checkip = function(ip, default) + local chunks = {ip:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")} + if (#chunks == 4) then + return ip + else + return default + end + end + + net:setip(dhcp,checkip(fields[3], "192.168.0.1"), checkip(fields[4], "255.255.255.0")) + if (fields[1] == INTF_GATEWAY) then + net:setip(dhcp,checkip(fields[3], "192.168.0.1"), "0.0.0.0") + net:justaddress() + end + +end + +function NetWindow:loadinterfaces() + local list = self:getById("network-list") + for _, t in pairs(interfaces) do + local intf = { } + table.insert(intf, t["name"]) + if not t["dhcp"] then + table.insert(intf, "no") + else + table.insert(intf, "yes") + end + table.insert(intf,t["addr"]) + table.insert(intf, t["netmask"]) + local item = {} + table.insert (item, intf) + list:addItem(item) + end +end + +local netwin = NetWindow:new +{ + Id = "network-window", + Title = APP_ID .. " " .. VERSION, + Orientation = "vertical", + Status = "hide", + HideOnEscape = true, + SizeButton = true, + hide = function(self) + ui.Window.hide(self) + app:switchwindow("network-window", "MainWindow") + end, + Children = + { + RescueGUIHeader:new + { + Description = APP_ID .. " " .. VERSION, + Image = LogoImage, + }, + ui.ListView:new + { + VSliderMode = "auto", + HSliderMode = "auto", + Headers = {L.NETWORK_INTERFACE, L.DHCP, L.IP_ADDRESS, L.NETMASK}, + Child = ui.Lister:new + { + Id = "network-list", + SelectMode = "single", + ListObject = List:new + { + Id = "network-objects", + Items = {} + }, + onSelectLine = function(self) + ui.Lister.onSelectLine(self) + local line = self:getItem(self.SelectedLine) + if line then + self.Window:setRecord(line[1]) + end + -- check for gateway and disable dhcp + end, + } + }, + NetAddress:new + { + Id = "network-fields" + }, + ui.Group:new + { + Orientation = "horizontal", + SameSize = true, + Children = + { + ui.Button:new + { + Id = "apply-button", + Text = L.APPLY, + onClick = function(self) + ui.Button.onClick(self) + local list = self:getById("network-list") + local line = list:getItem(list.SelectedLine) + if not line then + print("No interface selected, no change") + return + end + local intf + local name = "" + if (line) then + intf = line[1] + name = intf[1] + end + net = self:getById("network-fields") + local dhcp, ip, netmask = net:getip() + modinterface(name, dhcp, ip, netmask) + line = {} + table.insert (line, name) + if (dhcp) then + table.insert (line, "yes") + else + table.insert (line, "no") + end + table.insert (line, ip) + table.insert (line, netmask) + local newval = { line } + list:changeItem(newval, list.CursorLine) + list:rethinkLayout(true, 1) + ifup(interfaces) + + -- Reload values from kernel to check if they were set + loadnetinterfaces() + + if SAVEIPADDRESS then + app:addCoroutine(function() + result = app:easyRequest(false, L.CONFIRM_SAVE_NETWORK, L.SAVE, L.CANCEL) + if result == 1 then + SAVEIPADDRESS(interfaces) + end + end) + end + end + } + } + } + } +} + +------------------------------------------------------------------------------- +-- Application: +------------------------------------------------------------------------------- + +app = ui.Application:new +{ + ProgramName = "SWUpdate Rescue GUI", + Author = "Stefano Babic", + Copyright = "Copyright © 2018, Stefano Babic", + ApplicationId = APP_ID, + AuthorStyleSheets = STYLESHEETS, + + SWUpdateCfg = { + + }, + + setup = function(self) + ui.Application.setup(self) + self.Application:addInputHandler(ui.MSG_USER, self, self.msgUser) + end, + cleanup = function(self) + ui.Application.cleanup(self) + self.Application:remInputHandler(ui.MSG_USER, self, self.msgUser) + end, + msgUser = function(self,msg) + local prog = msg[-1] + self:switchtoprog(self) + t = {} + for field in string.gmatch(prog, "%S+" ) do + k,v = string.match(field, "(%a+)=\'(.*)\'") + t[k] = v + print (k, "=", v) + end + self:updateProgress(t) + end, + switchwindow = function(self, currentwin, newwin) + w = self:getById(currentwin) + if (not w) then + print("Window ", currentwin, " Not found !!") + return + end + w:setValue("Status", "hide") + w = self:getById(newwin) + if (not w) then + print("Window ", newwin, " Not found !!") + return + end + w:setValue("Status", "show") + end, + switchtoprog = function(self) + self:switchwindow("MainWindow", "progress-window") + end, + + updateProgress = function(self, prog) + if not prog then return end + for k,v in pairs(prog) do + print(k,v) + end + g = self:getById("slider-steps") + g:setValue("Max", tonumber(prog["nsteps"])) + g:setValue("Value", tonumber(prog["step"])) + g = self:getById("slider-step") + g:setValue("Value", tonumber(prog["percent"])) + + msg = self:getById("message-box") + msg:setValue("Text", prog["artifact"]) + status = tonumber(prog["status"]) + + g = self:getById("progress-caption") + + if status == STATUS_RUN then + g:setValue("Text", L.UPDATE_IN_PROGRESS) + g:setValue("Style", "font: ui-xx-large/bi;") + elseif status == STATUS_FAILURE then + g:setValue("Text", L.FAILURE) + g:setValue("Style", "font: ui-xx-large/bi; color: #ff0000;") + elseif status == STATUS_SUCCESS then + g:setValue("Text", L.SUCCESS) + g:setValue("Style", "font: ui-xx-large/bi; color: #00ff00;") + elseif status == STATUS_START then + g:setValue("Text", L.STARTING_UPDATE) + g:setValue("Style", "font: ui-xx-large/bi;") + else + + end + end, + Children = + { + + ui.Window:new + { + Orientation = "vertical", + Id = "MainWindow", + Children = + { + RescueGUIHeader:new + { + Description = APP_ID .. " " .. VERSION, + Image = LogoImage, + }, + + Group:new + { + Orientation = "vertical", + HAlign = "center", + Children = + { + Button:new + { + Text = L.INSTALL_FROM_FILE, + Width = "fill", + HAlign = "center", + VAlign = "center", + Height = "auto", + InitialFocus = true, + Style = "font: ui-xx-large/bi;", + + onClick = function(self) + local app = self.Application + app:addCoroutine(function() + local status, path, files = app:requestFile + { + Center = true, + Height = 200, + BasePath = "/media", + Path = "*.swu", + SelectMode = "single", + DisplayMode = "all" + } + print (status, path, files) + if (status == "selected" and files[1]) then + local swufile = files[1] + if (swufile) then + os.execute("swupdate-client /media" .. path .. "/" .. swufile .. " &") + end + end + end) + end + }, + ui.Spacer:new { }, + Button:new + { + Text = L.NETWORK_SETUP, + HAlign = "center", + VAlign = "center", + Width = "fill", + Height = "auto", + Style = "font: ui-xx-large/bi;", + onClick = function(self) + app:switchwindow("MainWindow", "network-window") + end, + }, + + Button:new + { + Text = L.REBOOT, + HAlign = "center", + Width = "fill", + Height = "auto", + Style = "font: ui-xx-large/bi;", + onClick = function(self) + if not NOREBOOT then + os.execute ("/sbin/reboot") + else + print("Just for test, reboot simulated") + end + end + } + } + }, + ui.Spacer:new { }, + Group:new + { + Id = "interfaces-group", + Orientation = "vertical", + HAlign = "center", + Children = + { + + } + } + } + } + } +} + + +------------------------------------------------------------------------------- +-- Progress task conecting to swupdate +------------------------------------------------------------------------------- + +local progtask = exec.run(function() + -- child task: + local exec = require "tek.lib.exec" + local ui = require "tek.ui" + local sw = require "lua_swupdate" + prog = sw:progress() + exec.sendmsg("*p", "Test") + + while true do + r = prog:receive() + local tmp = "" + for k,v in pairs (r) do + tmp = tmp .. " " .. k .. "='" .. v .. "'" + end + exec.sendport("*p", "ui", tmp) + end +end) + +------------------------------------------------------------------------------- +-- Run application +------------------------------------------------------------------------------- + +loadnetinterfaces(app) +-- ui.Application.connect(progwin) +app:addMember(netwin) +ui.Application.connect(netwin) +netwin:loadinterfaces() + +progwin:setValue("Status", "hide") +netwin:setValue("Status", "hide") +app:addMember(progwin) +app:run() + +progtask:terminate() + +app:hide() +app:cleanup() \ No newline at end of file diff --git a/SWUpdateGUI_ipaddress.lua b/SWUpdateGUI_ipaddress.lua new file mode 100755 index 0000000..96f3edf --- /dev/null +++ b/SWUpdateGUI_ipaddress.lua @@ -0,0 +1,145 @@ +#!/usr/bin/env lua +-- (C) Copyright 2018 +-- Stefano Babic, DENX Software Engineering, sbabic@denx.de. +-- +-- SPDX-License-Identifier: GPL-2.0-or-later +-- + +ui = require "tek.ui" +local Button = ui.Button + +local VerboseCheckMark = ui.CheckMark:newClass { _NAME = "_vbcheckmark" } +local VerboseRadioButton = ui.RadioButton:newClass { _NAME = "_vbradiobutton" } + +------------------------------------------------------------------------------- +-- Create IPAddress class: +------------------------------------------------------------------------------- +IPAddress = ui.Group:newClass { _NAME = "_ipaddress"} + +NetAddress = ui.Group:newClass { _NAME = "_netaddress"} + +function IPAddress.getAddress(class,self) + return self.address +end + + +function IPAddress.new(class, self) + self = self or { } + self.Class = "ipaddress" + Orientation = "vertical" + + grp = ui.Group.new(class, self) + self.buttons = {} + for i = 1, 4 do + button = ui.Button:new(addr) + + self.buttons[i] = ui.Button:new { + onClick = function(self) + val = tonumber(self.Text) + 1 + if (val > 255) then + val = 1 + end + self:setValue("Text", tostring(val)) + end + } + self.buttons[i].Text= "1" + grp:addMember(self.buttons[i]) + end + + return grp +end + +function IPAddress:setip(address) + local count = 1 + self.address = address + local chunks = {address:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")} + if not (#chunks == 4) then + return + end + for ipx in address:gmatch("([^.]+),?") do + self.buttons[count]:setValue("Text", ipx) + count = count + 1 + if (count > 4) then + break + end + end +end + +function IPAddress:getip() + addr = "" + for i = 1,4 do + local tmp = self.buttons[i].Text + if (i == 1) then + addr = tmp + else + addr = addr .. "." .. tmp + end + end + return addr +end + +function IPAddress:enable(enable) + for i = 1, 4 do + self.buttons[i]:setValue("Disabled", not enable) + end +end + + +function NetAddress.new(class, self, defaultip, defaultnetmask) + self = self or { } + self.Class = "netaddress" + + print(defaultip, defaultnetmask) + self.defaultip = defaultip + self.defaultnetmask = defaultnetmask + self.grp = ui.Group.new(class, self) + self.dhcp = ui.CheckMark:new + { + Text = "dhcp", + Id = "dhcp", + Selected = true, + onSelect = function(self) + ui.RadioButton.onSelect(self) + self.parent:enable(self.Selected) + end + } + self.dhcp.parent = self + + self.grp:setValue("Orientation", "vertical") + self.grp:addMember(self.dhcp) + + self.ip = IPAddress:new { Legend = "IP Address"} + self.grp:addMember(self.ip) + self.grp:addMember(ui.Spacer:new { }) + self.netmask = IPAddress:new { Legend = "Netmask"} + self.grp:addMember(self.netmask) + + return self.grp + +end + +function NetAddress:enable(dhcp) + self.dhcp:setValue("Selected", dhcp) + self.ip:enable(not dhcp) + self.netmask:enable(not dhcp) +end + +function NetAddress:justaddress() + self.dhcp:setValue("Disabled", true) + self.ip:enable(true) + self.netmask:enable(false) +end + +function NetAddress:setip(endhcp, ip, netmask, gateway) + self.dhcp:setValue("Disabled", false) + self.dhcp:setValue("Selected", endhcp) + self.ip:enable(self.ip, not endhcp) + self.netmask:enable(self.netmask, not endhcp) + + self.ip:setip(ip) + self.netmask:setip(netmask) +end + +function NetAddress:getip() + return self.dhcp.Selected, self.ip:getip(), self.netmask:getip() +end diff --git a/SWUpdateGUI_osinterface.lua b/SWUpdateGUI_osinterface.lua new file mode 100644 index 0000000..d71edaf --- /dev/null +++ b/SWUpdateGUI_osinterface.lua @@ -0,0 +1,24 @@ +-- (C) Copyright 2018 +-- Stefano Babic, DENX Software Engineering, sbabic@denx.de. +-- +-- SPDX-License-Identifier: GPL-2.0-or-later + +------------------------------------------------------------------------------- +-- Use standard tool to setup Network +------------------------------------------------------------------------------- + +function ifup(setup) + for _,intf in pairs(setup) do + local cmd + if intf["name"] == "DefaultGateway" then + cmd = "route del default;route add default gw " .. intf["addr"] + else + if intf["dhcp"] then + cmd = "udhcpc -n -i " .. intf["name"] + else + cmd = "ifconfig " .. intf["name"] .. " " .. intf["addr"] .. " netmask " .. intf["netmask"] + end + end + os.execute(cmd) + end +end \ No newline at end of file diff --git a/common.lua b/common.lua new file mode 100644 index 0000000..f80f773 --- /dev/null +++ b/common.lua @@ -0,0 +1,38 @@ +-- (C) Copyright 2018 +-- Stefano Babic, DENX Software Engineering, sbabic@denx.de. +-- +-- SPDX-License-Identifier: GPL-2.0-or-later +-- +-- Set default values, they can be overwritten +-- in the config.lua file +------------------------------------------------------------------------------- +-- Create Window common Header +------------------------------------------------------------------------------- +local ui = require "tek.ui" + +RescueGUIHeader = ui.Group:newClass { _NAME = "_guiheader"} + +function RescueGUIHeader.new(class, self) + self = self or { } + self.Class = "guiheader" + grp = ui.Group.new(class, self) + grp.Orientation = "vertical" + self.title = ui.Text:new + { + Text = self.Description or "", + Style = "font: ui-xx-large/bi;" + } + + self.logo = ui.ImageWidget:new + { + Width = "fill", + Height = "100", + Mode = "inert", + Image = self.Image, + HAlign = "center", + } + grp:addMember(self.title) + grp:addMember(self.logo) + + return grp +end \ No newline at end of file diff --git a/config.lua b/config.lua new file mode 100644 index 0000000..33dd3b6 --- /dev/null +++ b/config.lua @@ -0,0 +1,9 @@ +APP_ID = "SWUpdate Rescue GUI" +LOGO = "logo.png" +NOREBOOT = true +NETWORK_INTERFACES = {"eth0", "eth1"} +LANG= "de" +STYLESHEETS = "industrial" +function SAVEIPADDRESS(setup) + print("SAVEIPADDRESS called") +end diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e38b304675c01171ecd1d85519cfe7c4bf92ee24 GIT binary patch literal 11983 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 literal 0 HcmV?d00001 diff --git a/tek/ui/locale/SWUpdate/SWUpdate-GUI/de b/tek/ui/locale/SWUpdate/SWUpdate-GUI/de new file mode 100644 index 0000000..4320a75 --- /dev/null +++ b/tek/ui/locale/SWUpdate/SWUpdate-GUI/de @@ -0,0 +1,11 @@ +APPLY = "Änderungen speichern"; +DHCP = "DHCP"; +IP_ADRESSE = "IP Adresse"; +NUMBER_OF_STEPS = "Anzahl der Schritte"; +CURRENT_STEP = "Aktueller Schritt"; +INSTALL_FROM_FILE = "Installierung von USB"; +NETWORK_SETUP = "Netzwerk Einstellungen"; +NETMASK = "Netzwerksmaske"; +REBOOT = "Neustart"; +NETWORK_INTERFACE = "Netzwerksschnittstelle"; +UPDATE_IN_PROGRESS = "Aktualisierung läuft";