Newer
Older
ibsystem / ibmanager / logic / scripts / Updater.lua
--    ======================
--    Description
--    ======================
--
--    Logika umożliwiająca aktualizację ibpakietu
--
--    ======================
--    Parameters
--    ======================
--
--    input.update.trigger                    - wprowadzenie "1" powoduje rozpoczęcie procesu aktualizacji
--
--    input.ibpackage.password                - hasło ibpakietu
--
--    setting.checkout.time                   - czas co jaki będzie sprawdzane, czy aktualizacja jest dostępna [s]
--
--    setting.checkout.timeout                - timeout dla procedury sprawdzania sprawdzania czy aktualizacja jest dostępna [s]
--
--    setting.update.timeout                  - timeout dla procedury aktualizacji [s]
--
--    setting.dev                             - uwzględniaj również wersje developerskie
--                                              0 - nie uwzględniaj wersji developerskich (tylko wersje release)
--                                              1 - uwzględniaj wersje developerskie
--
--    counter.ibpackage.update.err            - kody błędów związane z aktualizacją ibpakietu
--                                              0 - wszystko OK, można przeprowadzić aktualizację lub instalację
--                                              1 - nie wprowadzono jeszcze hasła
--                                              2 - błędny nr seryjny lub hasło ibpakietu
--                                              3 - nie znaleziono softu w bazie
--                                              4 - ibpakiet nie jest uprawniony do instalacji wybranego softu
--                                              5 - soft nie jest aktywny. Nie można go instalować.
--                                              6 - wygasł abonament niezbędny dla aktualizacji
--                                              7 - licencja ibpakietu została unieważniona (revoked)
--                                              8 - ibpakiet nie jest zarejestrowany, aktualizacja będzie możliwa dopiero po zarejestrowaniu
--                                              100 - timeout
--
--    counter.ibpackage.serial                - nr seryjny ibpakietu
--
--    counter.software.name                   - nazwa aktualnego softu
--
--    counter.software.version.current        - wersja aktualnego softu
--
--    counter.software.version.next           - wersja kolejnego softu mozliwego do aktualizacji
--
--    counter.software.updatable              - jeżeli "1" to jest możliwa aktualizacja
--
--    counter.software.changelog.url          - link do changeloga dotyczącego aktualizacji
--
--    counter.checkout.downcounter            - licznik powiązany z setting.checkout.time
--
--    counter.os                              - identyfikator systemu operacyjnego
--
--    Logic uses lua language to implement own behaviour
--
--    ======================
--    mandatory variables
--    ======================
--
--    Logic expects following mandatory variables:
--
--    reload.trigger                          - causes reloading lua script
--
--    memcnt                                  - current amount of memory used by lua in bytes
--
--    Logic expects following kv settings:
--
--    LuaScriptPath                           - path to the lua script - must be absolute
--
--    ======================
--    ChangeLog
--    ======================
--
--    2022-08-24 ver 1.2.3
--    + obsługa systemu android-aarch64
--
--    2022-08-24 ver 1.2.2
--    + obsługa wersji developerskich
--
--    2022-06-16 ver 1.1.1
--    # log() lua funciton
--
--    2022-05-28 ver 1.0.0
--    # First release
--
--
-- user can use some functions provided by ibmanager.

-- ibmanager provides following functions to use:
--
-- function returns value of required ibmanager variable
-- @param fullName - string - variable name - name of variable of which value must be returned, for example "rs.0.id.255.input.t.0.value"
-- @return         - string or integer - variable value or "nil" if variable not exist or is not readable
--
-- getValue(fullName)


-- function set value of given ibmanager variable
-- @param fullName - string - variable name - name of variable of which value we want to set, for example "rs.0.id.255.input.t.0.value"
-- @param value    - string, int or boolean - value to set
-- @return         - nothing
--
-- setValue(fullName, value)


-- function returns value of required ibmanager variable
-- @param key      - placed in xml logic configuration file:
--                   * as attribute: Name, in Var, RemoteVar and ImportVar elements in the case of stand alone variables
--                   * as concatenation of two attributes: ListName.Postfix in VarListItem, RemoteVarListItem and ImportVarListItem elements
--                     in the case of variables that are placed in lists
-- @return         - string or integer - variable value or "nil" if variable not exist or is not readable
--
-- getLogicValue(key)


-- function set value of given ibmanager variable
-- @param key      - placed in xml logic configuration file:
--                   * as attribute: Name, in Var, RemoteVar and ImportVar elements in the case of stand alone variables
--                   * as concatenation of two attributes: ListName.Postfix in VarListItem, RemoteVarListItem and ImportVarListItem elements
--                     in the case of variables that are placed in lists
-- @param value    - string, int or boolean - value to set
-- @return         - nothing
--
-- setLogicValue(key, value)

-- function returns two sections of kvsettings from xml configuration file
-- returned value is two element table, each of these elements is table too.
-- indices of returned table are strings and equal "instance" and "global"
-- values of returned tables are tables and contain KVsettings for applicable section.
-- nested tables have form key = value, where key is index in nested table and value is value.
-- example: {"instance" = {"ikey1" = "ivalue1", "ikey2" = "gvalue2"}, "global" = {"gkey1" = "gvalue1", "gkey2" = "gvalue2", "gkey3" = "gvalue3"}}
-- @return          - two dimensional array - kvsettings for global and instance sections
-- getKvSettings()


-- function schedules alert to send.
-- rules are defined in separated alert configuration files and are described in ibmanager instruction manual
-- @param id - alert identifier - must be defined in current logic configuration file in section: <Alert Id="any_identifier" ...
-- scheduleAlert(id)

-- function cancels alert sending, if was previously scheduled. if not then only wakes up alerts handling thread, so if there is no need to call this function, then do not call it.
-- rules are defined in separated alert configuration files and are described in ibmanager instruction manual
-- @param id - alert identifier - must be defined in current logic configuration file in section: <Alert Id="any_identifier" ...
-- cancelAlert(name)

-- function returns table, containing Variables that belongs to required list.
-- @param listName - Name attribute of VarList, RemoteVarList or ImportVarList elemetnst in configurationfile
-- @return array of key-value pairs. Key - variable postfix, Value - Variable value
-- getVarList(listName)

-- function returns monotonic system clock value, that elapsed since specific epoch
-- returned time is expressed in milliseconds.
-- getClock()

-- function logs message to file, if defined in configuration file log level is less than passed to this function
-- @param logLevel - one of:
--        LogLevel.TraceLo
--        LogLevel.Trace
--        LogLevel.TraceHi
--        LogLevel.DebugLo
--        LogLevel.Debug
--        LogLevel.DebugHi
--        LogLevel.Info
--        LogLevel.Notice
--        LogLevel.Warning
--        LogLevel.Error
--        LogLevel.Critical
-- @param logMessage - string to log
-- log(logLevel, logMessage)


-- ibmanager provides following global variables:

-- logic type, (in this case it will always be "Lua") - the same as in logic configuration file in section: <Logic Type="Lua" ...
-- LOGIC_TYPE

-- logic version, the same as in logic configuration file in section: <Logic ... Version="x.y.z" ...
-- LOGIC_VERSION

-- logic sub-type, the same as in logic configuration file in section: <Logic ... SubType="Hysteresis" ...
-- LOGIC_SUBTYPE

-- logic sub-version, the same as in logic configuration file in section: <Logic ... SubVersion="x.y.z" ...
-- LOGIC_SUBVERSION

-- logic instance name - the same as in logic configuration file, in section: <Instance Name="0">
-- LOGIC_INSTANCE_NAME

-- add script directory to package path
package.path = package.path .. ";./logic/scripts/utils/?.lua";

-- use script - without .lua extension - delta class
require("State");
require("DownCounter");

SUPPORTED_SUBLOGIC_TYPE = "Updater";
SUPPORTED_SUBLOGIC_VERSION = "1.2.3";
g_versionChecked = false;

-- entry point to the logic
-- @param firstCall - tells if logic is called first time
-- @return        - nothing

-- jaki os: https://gist.github.com/soulik/82e9d02a818ce12498d1
-- get_os_name(), funtion to return current OS name and architecture
-- Copyright Philippe Fremy 2017

-- This code is based on the following Gist from Soulik (https://gist.github.com/soulik)
-- https://gist.github.com/soulik/82e9d02a818ce12498d1
-- Initial license was unspecified so I am assuming public domain

local function getOS()
    -- Return two strings describing the OS name and OS architecture.
    -- For Windows, the OS identification is based on environment variables
    -- On unix, a call to uname is used.
    -- 
    -- OS possible values: Windows, Linux, Mac, BSD, Solaris
    -- Arch possible values: x86, x86864, powerpc, arm, mips
    -- 
    -- On Windows, detection based on environment variable is limited
    -- to what Windows is willing to tell through environement variables. In particular
    -- 64bits is not always indicated so do not rely hardly on this value.

    local raw_os_name, raw_arch_name = '', ''

    -- LuaJIT shortcut
    if jit and jit.os and jit.arch then
        raw_os_name = jit.os
        raw_arch_name = jit.arch
        -- print( ("Debug jit name: %q %q"):format( raw_os_name, raw_arch_name ) )
    else
        if package.config:sub(1,1) == '\\' then
            -- Windows
            local env_OS = os.getenv('OS')
            local env_ARCH = os.getenv('PROCESSOR_ARCHITECTURE')
            -- print( ("Debug: %q %q"):format( env_OS, env_ARCH ) )
            if env_OS and env_ARCH then
                raw_os_name, raw_arch_name = env_OS, env_ARCH
            end
        else
            -- other platform, assume uname support and popen support
            raw_os_name = io.popen('uname -s','r'):read('*l')
            raw_arch_name = io.popen('uname -m','r'):read('*l')
        end
    end

    raw_os_name = (raw_os_name):lower()
    raw_arch_name = (raw_arch_name):lower()

    --print( ("Debug: %q %q"):format( raw_os_name, raw_arch_name) )

    local os_patterns = {
        ['windows']     = 'Windows',
        ['linux']       = 'linux',
        ['osx']         = 'Mac',
        ['mac']         = 'Mac',
        ['darwin']      = 'Mac',
        ['^mingw']      = 'Windows',
        ['^cygwin']     = 'Windows',
        ['bsd$']        = 'BSD',
        ['sunos']       = 'Solaris',
    }
    
    local arch_patterns = {
        ['^x86$']           = 'x86',
        ['i[%d]86']         = 'x86',
        ['amd64']           = 'x86_64',
        ['x86_64']          = 'x86_64',
        ['x64']             = 'x86_64',
        ['power macintosh'] = 'powerpc',
        ['^arm']            = 'arm',
        ['^mips']           = 'mips',
        ['i86pc']           = 'x86',
        ['aarch64']         = 'aarch64',
    }

    local os_name, arch_name = 'unknown', 'unknown'

    for pattern, name in pairs(os_patterns) do
        if raw_os_name:match(pattern) then
            os_name = name
            break
        end
    end
    for pattern, name in pairs(arch_patterns) do
        if raw_arch_name:match(pattern) then
            arch_name = name
            break
        end
    end
    
    local res = os_name .. "-" .. arch_name

    if (res == 'linux-aarch64') then
      return 'android-aarch64'
    else
      return res
    end
end

g_checkoutDowncounter = nil;

g_settingCheckoutTimeout = nil;
g_settingUpdateTimeout = nil;

g_counterSoftwareVersionNext = "";
g_counterSoftwareUpdatable = nil;
g_counterIbpackageSerial = nil;
g_counterSoftwareName = nil;
g_counterSoftwareVersionCurrent = nil;

g_checkoutTimeoutTimer = nil;
g_updateTimeoutTimer = nil;
g_os_name = nil;

g_inputIbpackagePassword = '';

function fileExists(name)
   local f=io.open(name,"r")
   if f~=nil then io.close(f) return true else return false end
end

function lunixArm_getSoftInfo()
  g_counterSoftwareVersionNext = "";
  g_counterSoftwareUpdatable = 0;

  local dirName = "/ibsystem-tmp/tmp/updater/" .. LOGIC_INSTANCE_NAME .. "_getSoftInfo";
  local fileName = dirName .. "/getsoftinfo.sh";
  local logFileName = dirName .. "/getsoftinfo.sh.log";
  local outputFileName = dirName .. "/getsoftinfo.out";
  local i = 0;
  local downloadprogress = 0;

  os.execute("mkdir -p " .. dirName)
  os.execute("rm -rf " .. dirName .. "/*")
  os.execute("touch " .. fileName)
  os.execute("chmod 755 " .. fileName)
  
  local find_option = "next"

  if (getLogicValue("setting.dev") == 1) then
    find_option = "next_dev"
  end

  local content =
[[
#!/bin/sh
curl "https://ibsystem.org/getinfo.php?result=softversion&softname=]] .. g_counterSoftwareName .. [[&currentver=]] .. g_counterSoftwareVersionCurrent .. [[&find=]] .. find_option .. [[" > ]] .. outputFileName .. [[
]]

  local file = io.open(fileName, "w")
  file:write(content)
  file:close()

  os.execute(fileName .. " > " .. logFileName .. " 2>&1 &")

  repeat
    os.execute("sleep 1")

    -- resetuje wszystkie niepowiązane liczniki
    g_updateTimeoutTimer:reset();
    g_checkoutDowncounter:reset();

    -- sprawdza postęp z cron'a
    local resFile = io.popen([[sed 's/\r/\n/g' < ]] .. logFileName .. [[ | tail -n 1]])
    local resContent =  assert(resFile:read(_VERSION <= "Lua 5.2" and "*a" or "a"))
    resFile:close()
    i = 0;
    --print(resContent)
    for value in string.gmatch(resContent, "%S+") do
      -- print(i, value)
      if (i == 0) then
        downloadprogress = value
      end
      i = i + 1;
    end

    -- print(downloadprogress)

    -- obsługuje timeouta z logiki
    if g_checkoutTimeoutTimer:elapsed() then
      log(LogLevel.Info, "Checkout timeout!")
      return;
    end

  until tonumber(downloadprogress) == 100

  local fh = assert(io.open(outputFileName, "rb"))
  local resContent = assert(fh:read(_VERSION <= "Lua 5.2" and "*a" or "a"))
  fh:close()

  -- parsuje wartości key=value rozdzielone średnikiem (;)
  for key, value in string.gmatch(resContent, "(%S-)%s*=%s*(.-);") do
    if (key == 'next') then
      g_counterSoftwareVersionNext = value
    elseif (key == 'updatable') then
      g_counterSoftwareUpdatable = value
    end
    --print(key .. " -> " .. value)
  end

end

function lunixArm_checkPermissions()
  local dirName = "/ibsystem-tmp/tmp/updater/" .. LOGIC_INSTANCE_NAME .. "_checkPermissions";
  local fileName = dirName .. "/checkpermissions.sh";
  local logFileName = dirName .. "/getsoftinfo.sh.log";
  local outputFileName = dirName .. "/checkpermissions.out";
  local i = 0;
  local downloadprogress = 0;

  os.execute("mkdir -p " .. dirName)
  os.execute("rm -rf " .. dirName .. "/*")
  os.execute("touch " .. fileName)
  os.execute("chmod 755 " .. fileName)

  setLogicValue("counter.ibpackage.update.err", "100");

  local content =
[[
#!/bin/sh
curl --insecure -u ]] .. g_counterIbpackageSerial .. [[:]] .. g_inputIbpackagePassword .. [[ -H "Accept: application/product+xml" "https://ibsystem.org/getinfo.php?result=permissions&softname=]] .. g_counterSoftwareName .. [[" > ]] .. outputFileName .. [[
]]

  local file = io.open(fileName, "w")
  file:write(content)
  file:close()

  os.execute(fileName .. " > " .. logFileName .. " 2>&1 &")

  repeat
    os.execute("sleep 1")

    -- resetuje wszystkie niepowiązane liczniki
    g_updateTimeoutTimer:reset();
    g_checkoutDowncounter:reset();

    -- sprawdza postęp z cron'a
    local resFile = io.popen([[sed 's/\r/\n/g' < ]] .. logFileName .. [[ | tail -n 1]])
    local resContent =  assert(resFile:read(_VERSION <= "Lua 5.2" and "*a" or "a"))
    resFile:close()
    i = 0;
    --print(resContent)
    for value in string.gmatch(resContent, "%S+") do
      -- print(i, value)
      if (i == 0) then
        downloadprogress = value
      end
      i = i + 1;
    end

    -- print(downloadprogress)

    -- obsługuje timeouta z logiki
    if g_checkoutTimeoutTimer:elapsed() then
      log(LogLevel.Info, "Checkout timeout!")
      return;
    end

  until tonumber(downloadprogress) == 100

  local fh = assert(io.open(outputFileName, "rb"))
  local resContent = assert(fh:read(_VERSION <= "Lua 5.2" and "*a" or "a"))
  fh:close()

  -- parsuje wartości key=value rozdzielone średnikiem (;)
  for key, value in string.gmatch(resContent, "(%S-)%s*=%s*(.-);") do
    if (key == 'err') then
      setLogicValue("counter.ibpackage.update.err", value);
    end
    --print(key .. " -> " .. value)
  end

end

function lunixArm_update()
  local dirName = "/ibsystem-tmp/tmp/updater/" .. LOGIC_INSTANCE_NAME .. "_update";
  local fileName = dirName .. "/exeupdate.sh";
  local logFileName = dirName .. "/exeupdate.sh.log";
  local outputFileName = dirName .. "/install.tar.gz";
  local i = 0;
  local downloadprogress = 0;

  os.execute("mkdir -p " .. dirName)
  os.execute("rm -rf " .. dirName .. "/*")
  os.execute("touch " .. fileName)
  os.execute("chmod 755 " .. fileName)

  -- skrypt zapisujący główną paczkę instalacyjną
  local content =
[[
#!/bin/sh
curl --insecure -u ]] .. g_counterIbpackageSerial .. [[:]] .. g_inputIbpackagePassword .. [[ -H "Accept: application/product+xml" "https://ibsystem.org/getpackage.php?soft=]] .. g_counterSoftwareName .. [[&system=]] .. g_os_name .. [[&ver=]] .. g_counterSoftwareVersionNext .. [[&certs=0" --output "]] .. outputFileName .. [[" --max-time ]] .. g_settingUpdateTimeout .. [[
]]

  local file = io.open(fileName, "w")
  file:write(content)
  file:close()

  os.execute(fileName .. " > " .. logFileName .. " 2>&1 &")

  repeat
    os.execute("sleep 1")

    -- resetuje wszystkie niepowiązane liczniki
    g_checkoutTimeoutTimer:reset();
    g_checkoutDowncounter:reset();

    -- sprawdza postęp z cron'a
    local resFile = io.popen([[sed 's/\r/\n/g' < ]] .. logFileName .. [[ | tail -n 1]])
    local resContent =  assert(resFile:read(_VERSION <= "Lua 5.2" and "*a" or "a"))
    resFile:close()
    i = 0;
    --print(resContent)
    for value in string.gmatch(resContent, "%S+") do
      -- print(i, value)
      if (i == 0) then
        downloadprogress = value
      end
      i = i + 1;
    end

    -- obsługuje timeouta z logiki
    if g_updateTimeoutTimer:elapsed() then
      log(LogLevel.Info, "Update timeout!")
      return;
    end

  until tonumber(downloadprogress) == 100

  local updatescriptname = g_counterSoftwareName .. "-update-" .. g_counterSoftwareVersionCurrent .. "-to-" .. g_counterSoftwareVersionNext .. "-" .. g_os_name .. ".sh";
  --print (updatescriptname)

  outputFileName = dirName .. "/update.sh";

  -- skrypt zapisujący skrypt aktualizacji
  local content =
[[
#!/bin/sh
curl --insecure -u ]] .. g_counterIbpackageSerial .. [[:]] .. g_inputIbpackagePassword .. [[ -H "Accept: application/product+xml" "https://ibsystem.org/getpackage.php?soft=]] .. updatescriptname .. [[" --output "]] .. outputFileName .. [[" --max-time ]] .. g_settingUpdateTimeout .. [[ && chmod 0775 ]] .. outputFileName .. [[
]]

  local file = io.open(fileName, "w")
  file:write(content)
  file:close()

  os.execute(fileName .. " > " .. logFileName .. " 2>&1 &")

  repeat
    os.execute("sleep 1")

    -- resetuje wszystkie niepowiązane liczniki
    g_checkoutTimeoutTimer:reset();
    g_checkoutDowncounter:reset();

    -- sprawdza postęp z cron'a
    local resFile = io.popen([[sed 's/\r/\n/g' < ]] .. logFileName .. [[ | tail -n 1]])
    local resContent =  assert(resFile:read(_VERSION <= "Lua 5.2" and "*a" or "a"))
    resFile:close()
    i = 0;
    --print(resContent)
    for value in string.gmatch(resContent, "%S+") do
      -- print(i, value)
      if (i == 0) then
        downloadprogress = value
      end
      i = i + 1;
    end

    -- obsługuje timeouta z logiki
    if g_updateTimeoutTimer:elapsed() then
      log(LogLevel.Info, "Update timeout!")
      return;
    end

  until tonumber(downloadprogress) == 100

  os.execute(outputFileName .. " " .. dirName .. " > /ibsystem-tmp/update.log 2>&1 &")

end


function onLogicCall(firstCall)

  if not g_versionChecked then
    -- checking sublogic type and sublogic version
    if LOGIC_SUBTYPE ~= SUPPORTED_SUBLOGIC_TYPE then
      error("Wrong logic sub-type. expected " .. SUPPORTED_SUBLOGIC_TYPE .. " but used " .. LOGIC_SUBTYPE);
    end
    local versionWithoutBuild = string.match(LOGIC_SUBVERSION, "[0-9]+%.[0-9]+%.[0-9]+");
    if versionWithoutBuild ~= SUPPORTED_SUBLOGIC_VERSION then
      error("Wrong logic sub-version. expected " .. SUPPORTED_SUBLOGIC_VERSION .. " but used " .. LOGIC_SUBVERSION);
    end
    g_versionChecked = true;
  end

  local inputUpdateTrigger = getLogicValue("input.update.trigger");
  local inputIbpackagePassword = getLogicValue("input.ibpackage.password");
  local settingCheckoutTime = getLogicValue("setting.checkout.time");

  g_settingCheckoutTimeout = getLogicValue("setting.checkout.timeout");
  g_settingUpdateTimeout = getLogicValue("setting.update.timeout");

  g_counterIbpackageSerial = getLogicValue("counter.ibpackage.serial");
  g_counterSoftwareName = getLogicValue("counter.software.name");
  g_counterSoftwareVersionCurrent = getLogicValue("counter.software.version.current");

  -- g_counterSoftwareVersionNext = "";
  g_counterSoftwareUpdatable = 0;

  local checkoutDowncounter = 0;

  if firstCall then 
    setLogicValue("input.update.trigger", 0);
    setLogicValue("input.ibpackage.password", "");
  end


-- timers

  if g_checkoutTimeoutTimer == nil or firstCall then
    g_checkoutTimeoutTimer = DownCounter.create();
  end
  g_checkoutTimeoutTimer:updateParams(g_settingCheckoutTimeout * 1000);

  if g_updateTimeoutTimer == nil or firstCall then
    g_updateTimeoutTimer = DownCounter.create();
  end
  g_updateTimeoutTimer:updateParams(g_settingUpdateTimeout * 1000);


  if g_checkoutDowncounter == nil or firstCall then
    g_checkoutDowncounter = DownCounter.create();
  else
    g_checkoutDowncounter:updateParams(settingCheckoutTime * 1000);
  end

  if g_os_name == nil or firstCall then
    g_os_name = getOS()
  end


  if g_checkoutDowncounter:elapsed() then

    if ((g_os_name == 'linux-arm') or (g_os_name == 'android-aarch64')) then
      g_checkoutTimeoutTimer:reset();
      lunixArm_getSoftInfo();
    else
      log(LogLevel.Error, "unsupported OS: " .. g_os_name)
    end

    -- send logic variables to ibmanager
    setLogicValue("counter.software.version.next", g_counterSoftwareVersionNext);
    setLogicValue("counter.software.updatable", g_counterSoftwareUpdatable);

    g_checkoutDowncounter:reset();
  end

  checkoutDowncounter = g_checkoutDowncounter:timeTo0() / 1000;
  checkoutDowncounter = (checkoutDowncounter < 0) and 0 or checkoutDowncounter;

  if (inputIbpackagePassword == '') and (g_inputIbpackagePassword == '') then
    setLogicValue("counter.ibpackage.update.err", 1);
  end

  if (inputIbpackagePassword ~= g_inputIbpackagePassword) then
    g_inputIbpackagePassword = inputIbpackagePassword;

    if ((g_os_name == 'linux-arm') or (g_os_name == 'android-aarch64')) then
      g_checkoutTimeoutTimer:reset();
      lunixArm_checkPermissions();
    else
      log(LogLevel.Error, "unsupported OS: " .. g_os_name)
    end
  end

  if (inputUpdateTrigger == 1) then

    if ((getLogicValue("counter.ibpackage.update.err") == 0) and (getLogicValue("counter.software.updatable") == 1)) then

      if ((g_os_name == 'linux-arm') or (g_os_name == 'android-aarch64')) then
        g_checkoutTimeoutTimer:reset();
        g_updateTimeoutTimer:reset();
        g_checkoutDowncounter:reset();
        lunixArm_update();
      else
        log(LogLevel.Error, "unsupported OS: " .. g_os_name)
      end

    end

    setLogicValue("input.update.trigger", 0);
    setLogicValue("input.ibpackage.password", "");

  end

  -- send logic variables to ibmanager
  setLogicValue("counter.checkout.downcounter", checkoutDowncounter);
  setLogicValue("counter.software.changelog.url", "https://ibsystem.org/getinfo.php?result=changelog&softname=" .. g_counterSoftwareName .. "&currentver=" .. g_counterSoftwareVersionCurrent);
  setLogicValue("counter.os", g_os_name);

end