Language Bindings

Overview

In general, SWUpdate is agnostic to a particular language it is operated from, thanks to SWUpdate’s socket-based control and progress APIs for external programs. As long as the language of choice has proper socket (library) support, SWUpdate can be operated with it.

However, for convenience, a Lua language binding in terms of a shared library, currently lua_swupdate.so.0.1, is provided.

Lua Language Binding

The Lua language binding is realized in terms of the lua_swupdate module that defines three bindings, namely for the control interface, the progress interface, and a convenience function yielding a table holding all local network interfaces including their IP addresses and submasks.

The lua_swupdate Lua module interface specification that details what functionality is made available by bindings/lua_swupdate.c is found in bindings/lua_swupdate.lua, reproduced for convenience in Section Lua Language Binding Interface Specification. It serves as reference, for mocking purposes, and type checking thanks to the EmmyLua-inspired annotations.

Note that, depending on the filesystem location of the Lua binding’s shared library, Lua’s package.cpath may have to be adapted by setting the environment variable LUA_CPATH, modifying package.cpath prior to a require('lua_swupdate'), or , as last resort, using package.loadlib() instead of require('lua_swupdate').

Control Interface

The lua_swupdate module’s control interface binding conveniently makes SWUpdate’s socket-based control API available to pure Lua.

The binding is captured in the swupdate_control object that is returned by a call to swupdate.control(). This object offers the three methods connect(), write(<chunkdata>), and close():

The connect() method initializes the connection to SWUpdate’s control socket, sends REQ_INSTALL, and waits for ACK or NACK, returning the socket connection file descriptor, mostly for information purposes, or, in case of an error, nil plus an error message.

The artifact’s data can then be sent to SWUpdate via the write(<chunkdata>) method, returning true, or, in case of errors, nil plus an error message.

Finally, the close() method closes the connection to SWUpdate’s control socket after which it waits for SWUpdate to complete the update transaction and executes the post-install command, if given.

The following example snippet illustrates how to use the control interface binding:

local artifact = io.open("/some/path/to/artifact.swu", "rb" )
swupdate = require('lua_swupdate')
local ctrl = swupdate.control()
if not ctrl:connect() then
        -- Deliberately neglecting error message.
        io.stderr:write("Error connecting to SWUpdate control socket.\n")
        return
end

while true do
        local chunk = artifact:read(1024)
        if not chunk then break end
        if not ctrl:write(chunk) then
                -- Deliberately neglecting error message.
                io.stderr:write("Error writing to SWUpdate control socket.\n")
                break
        end
end

local res, msg = ctrl:close()
if not res then
        io.stderr:write(string.format("Error finalizing update: %s\n", msg))
end

Progress Interface

The lua_swupdate module’s progress interface binding conveniently makes SWUpdate’s socket-based progress API available to pure Lua.

The binding is captured in the swupdate_progress object that is returned by a call to swupdate.progress(). This object offers the three methods connect(), receive(), and close():

The connect() method connects to SWUpdate’s progress socket, waiting until the connection has been established. Note that it is only really required to explicitly call connect() to reestablish a broken connection as the swupdate_progress object’s instantiation already initiates the connection.

The receive() method returns a table representation of the struct progress_msg described in the progress interface’s API description.

The close() method deliberately closes the connection to SWUpdate’s progress socket.

IPv4 Interface

For convenience, the lua_swupdate module provides the ipv4() method returning a table holding the local network interfaces as the table’s keys and their space-separated IP addresses plus subnet masks as respective values.

Lua Language Binding Interface Specification

  1--[[
  2
  3    SWUpdate IPC Lua Module Interface.
  4
  5    Interface specification for the Lua IPC module.
  6    See: bindings/lua_swupdate.c
  7
  8    Copyright (C) 2022, Siemens AG
  9    Author: Christian Storm <christian.storm@siemens.com>
 10
 11    SPDX-License-Identifier: GPL-2.0-or-later
 12
 13--]]
 14
 15---@diagnostic disable: missing-return
 16---@diagnostic disable: unused-local
 17-- luacheck: no unused args
 18
 19
 20--- SWUpdate IPC Lua Module.
 21--- @class lua_swupdate
 22local lua_swupdate = {}
 23
 24
 25--- Get local IPv4 network interface(s) information.
 26--
 27-- The returned Table contains the network interface names
 28-- as keys and a space-separated IP address and subnet mask
 29-- as values, e.g., {['eth0']="192.168.1.1 255.255.255.0"}.
 30--
 31--- @return table<string, string>
 32lua_swupdate.ipv4 = function() end
 33
 34
 35--- @enum lua_swupdate.RECOVERY_STATUS
 36--- Lua equivalent of `RECOVERY_STATUS` as in `include/swupdate_status.h`.
 37lua_swupdate.RECOVERY_STATUS = {
 38    IDLE       = 0,
 39    START      = 1,
 40    RUN        = 2,
 41    SUCCESS    = 3,
 42    FAILURE    = 4,
 43    DOWNLOAD   = 5,
 44    DONE       = 6,
 45    SUBPROCESS = 7,
 46    PROGRESS   = 8
 47}
 48
 49
 50--- @enum lua_swupdate.sourcetype
 51--- Lua equivalent of `sourcetype` as in `include/swupdate_status.h`.
 52lua_swupdate.sourcetype = {
 53    SOURCE_UNKNOWN           = 0,
 54    SOURCE_WEBSERVER         = 1,
 55    SOURCE_SURICATTA         = 2,
 56    SOURCE_DOWNLOADER        = 3,
 57    SOURCE_LOCAL             = 4,
 58    SOURCE_CHUNKS_DOWNLOADER = 5
 59}
 60
 61
 62--- Lua equivalent of `struct progress_msg` as in `include/progress_ipc.h`.
 63--- @class lua_swupdate.progress_msg
 64--- @field status    lua_swupdate.RECOVERY_STATUS  Update status, one of `lua_swupdate.RECOVERY_STATUS`'s values
 65--- @field download  number  Downloaded data percentage
 66--- @field source    lua_swupdate.sourcetype  Interface that triggered the update, one of `lua_swupdate.sourcetype`'s values
 67--- @field nsteps    number  Total number of steps
 68--- @field step      number  Current step
 69--- @field percent   number  Percentage done in current step
 70--- @field artifact  string  Name of image to be installed
 71--- @field handler   string  Name of running Handler
 72--- @field info      string  Additional information about installation
 73
 74
 75--- SWUpdate progress socket binding.
 76--
 77-- The returned Class Table contains methods to
 78-- interact with SWUpdate's progress socket.
 79--
 80lua_swupdate.progress = function()
 81    return {
 82        --- Connect to SWUpdate's progress socket.
 83        --
 84        --- @param  self  table   This `lua_swupdate.progress` instance
 85        --- @return number | nil  # The connection handle or nil in case of error
 86        --- @return nil | string  # nil or an error message in case of error
 87        connect = function(self) end,
 88
 89        --- Receive data from SWUpdate's progress socket.
 90        --
 91        --- @param  self  table                        This `lua_swupdate.progress` instance
 92        --- @return table | lua_swupdate.progress_msg  # This `lua_swupdate.progress` instance on error or the received progress message
 93        --- @return nil                                # nil in case of error
 94        receive = function(self) end,
 95
 96        --- Close connection to SWUpdate's progress socket.
 97        --
 98        --- @param  self  table  This `lua_swupdate.progress` instance
 99        --- @return table        # This `lua_swupdate.progress` instance
100        close = function(self) end,
101    }
102end
103
104
105--- SWUpdate control socket binding.
106--
107-- The returned Class Table contains methods to
108-- interact with SWUpdate's control socket.
109--
110lua_swupdate.control = function()
111    return {
112        --- Connect to SWUpdate's control socket.
113        --
114        --- @param  self  table   This `lua_swupdate.control` instance
115        --- @return number | nil  # Connection handle or nil in case of error
116        --- @return nil | string  # nil or an error message in case of error
117        connect = function(self) end,
118
119        --- Write to connected SWUpdate control socket.
120        --
121        --- @param  self  table    This `lua_swupdate.control` instance
122        --- @param  data  string   Chunk data to write to SWUpdate's control socket
123        --- @return boolean | nil  # true or nil in case of error
124        --- @return nil | string   # nil or an error message in case of error
125        write = function(self, data) end,
126
127        --- Close connection to SWUpdate's control socket.
128        --
129        --- @param  self  table    This `lua_swupdate.control` instance
130        --- @return boolean | nil  # true or nil in case of error
131        --- @return nil | string   # nil or an error message in case of error
132        close = function(self) end,
133    }
134end
135
136
137return lua_swupdate