Suricatta daemon mode

Introduction

Suricatta is – like mongoose – a daemon mode of SWUpdate, hence the name suricatta (engl. meerkat) as it belongs to the mongoose family.

Suricatta regularly polls a remote server for updates, downloads, and installs them. Thereafter, it reboots the system and reports the update status to the server, based on an update state variable currently stored in bootloader’s environment ensuring persistent storage across reboots. Some U-Boot script logics or U-Boot’s bootcount feature may be utilized to alter this update state variable, e.g., by setting it to reflect failure in case booting the newly flashed root file system has failed and a switchback had to be performed.

Suricatta is designed to be extensible in terms of the servers supported as described in Section The Suricatta Interface. Currently, support for the hawkBit server is implemented via the hawkBit Direct Device Integration API alongside a simple general purpose HTTP server. The support for suricatta modules written in Lua is not a particular server support implementation but rather an option for writing such in Lua instead of C.

Running suricatta

After having configured and compiled SWUpdate with enabled suricatta support for hawkBit,

./swupdate --help

lists the mandatory and optional arguments to be provided to suricatta when using hawkBit as server. As an example,

./swupdate -l 5 -u '-t default -u http://10.0.0.2:8080 -i 25'

runs SWUpdate in suricatta daemon mode with log-level TRACE, polling a hawkBit instance at http://10.0.0.2:8080 with tenant default and device ID 25.

If multiple server support is compiled in, the -S / --server option or a server entry in the configuration file’s [suricatta] section selects the one to use at run-time. For convenience, when having support for just one server compiled-in, this is chosen automatically.

Note that on startup when having installed an update, suricatta tries to report the update status to its upstream server, e.g., hawkBit, prior to entering the main loop awaiting further updates. If this initial report fails, e.g., because of a not (yet) configured network or a currently unavailable hawkBit server, SWUpdate may exit with an according error code. This behavior allows one to, for example, try several upstream servers sequentially. If suricatta should keep retrying until the update status is reported to its upstream server irrespective of the error conditions, this has to be realized externally in terms of restarting SWUpdate on exit.

After an update has been performed, an agent listening on the progress interface may execute post-update actions, e.g., a reboot, on receiving DONE. Additionally, a post-update command specified in the configuration file or given by the -p command line option can be executed.

Note that at least a restart of SWUpdate has to be performed as post-update action since only then suricatta tries to report the update status to its upstream server. Otherwise, succinct update actions announced by the upstream server are skipped with an according message until a restart of SWUpdate has happened in order to not install the same update again.

The Suricatta Interface

Support for servers other than hawkBit or the general purpose HTTP server can be realized by implementing the “interfaces” described in include/channel.h and include/suricatta/server.h, the latter either in C or in Lua. The channel interface abstracts a particular connection to the server, e.g., HTTP-based in case of hawkBit. The server interface defines the logics to poll and install updates. See corelib/channel_curl.c / include/channel_curl.h and suricatta/server_hawkbit.{c,h} for an example implementation in C targeted towards hawkBit.

include/channel.h describes the functionality a channel has to implement:

typedef struct channel channel_t;
struct channel {
    ...
};

channel_t *channel_new(void);

which sets up and returns a channel_t struct with pointers to functions for opening, closing, fetching, and sending data over the channel.

include/suricatta/server.h describes the functionality a server has to implement:

typedef struct {
    server_op_res_t has_pending_action(int *action_id);
    server_op_res_t install_update(void);
    server_op_res_t send_target_data(void);
    unsigned int get_polling_interval(void);
    server_op_res_t start(const char *cfgfname, int argc, char *argv[]);
    server_op_res_t stop(void);
    server_op_res_t ipc(int fd);
    void (*help)(void);
} server_t;

These functions constituting a particular suricatta server implementation have to be registered for being selectable at run-time by calling register_server() (see include/suricatta/server.h) with a name and a server_t struct pointer implemented in a __attribute__((constructor)) marked function, see suricatta/server_hawkbit.c as example.

The type server_op_res_t is defined in include/suricatta/suricatta.h. It represents the valid function return codes for a server’s implementation.

In addition to implementing the particular channel and server, the suricatta/Kconfig file has to be adapted to include a new option so that the new implementation becomes selectable in SWUpdate’s configuration. In the simplest case, adding an option like the following one for hawkBit into the menu "Server" section is sufficient.

config SURICATTA_HAWKBIT
    bool "hawkBit support"
    default y
    select JSON
    help
      Support for hawkBit server.
      https://projects.eclipse.org/projects/iot.hawkbit

Having included the new server implementation into the configuration, edit suricatta/Makefile to specify the implementation’s linkage into the SWUpdate binary, e.g., for the hawkBit example implementation, the following lines add server_hawkbit.o to the resulting SWUpdate binary if SURICATTA_HAWKBIT was selected while configuring SWUpdate.

ifneq ($(CONFIG_SURICATTA_HAWKBIT),)
obj-$(CONFIG_SURICATTA) += server_hawkbit.o
endif

Support for wfx

The wfx server is supported by the Lua Suricatta module suricatta/server_wfx.lua (cf. Section Support for Suricatta Modules in Lua). Specifically, it implements a binding to the Device Artifact Update (DAU) workflow family.

If enabled via CONFIG_SURICATTA_WFX, the wfx Lua Suricatta module is embedded into the SWUpdate binary so that no extra deployment steps are required. Note that this is purely a convenience shortcut for the installation of a Lua Suricatta module as described in Support for Suricatta Modules in Lua.

Job Definition

As being a general purpose workflow executor, wfx doesn’t impose a particular job definition nor schema, except that it’s in JSON format. Instead, the job definition is a contract between the operator creating jobs, each possibly following a different workflow, and the client(s) executing those jobs in lock-step with the wfx.

The wfx Lua Suricatta module understands job definitions as in the following example (see job.definition.json_schema in suricatta/server_wfx.lua):

{
    "version": "1.0",
    "type": ["firmware", "dummy"],
    "artifacts": [
        {
            "name": "Example Device Firmware Artifact",
            "version": "1.1",
            "uri": "http://localhost:8080/download/example_artifact.swu"
        }
    ]
}

The type list field allows to label update jobs. Labels are sent :-concatenated to the progress interface on Update Activation. The only predefined label firmware instructs the wfx Lua Suricatta module to record an installed update to the bootloader environment (see Bootloader Interface). Within the artifacts list, only the uri field is strictly required for each artifact while the fields name and version are used for informational purposes, if provided. Further fields, including top-level fields, are ignored on purpose and may be freely used, e.g., to enrich the job definition with metadata for update management.

Since wfx is not concerned with the job definition except for conveying it to the client (i.e. SWUpdate), it can be adapted to specific needs by feeding a different job definition into the wfx on job creation and adapting the verification and job handling methods in the wfx Lua Suricatta module’s job.definition = {...} Table.

Workflows

The two Device Artifact Update (DAU) workflows wfx.workflow.dau.direct and wfx.workflow.dau.phased are supported by the wfx Lua Suricatta module. Hint: Use wfx’s wfx-viewer command line tool to visualize the YAML workflows in SVG or PlantUML format.

For each transition in a workflow for which the eligible field contains CLIENT, e.g.,

transitions:
  - from: <FROM_STATE>
    to: <TO_STATE>
    eligible: CLIENT

there has to be a matching transition execution function defined in the wfx Lua Suricatta module. It executes the client actions to go from state <FROM_STATE> to state <TO_STATE> and finally sends the new job status to the wfx, updating it:

job.workflow.dispatch:set(
    "<FROM_STATE>",
    "<TO_STATE>",
    --- @param  self  job.workflow.transition
    --- @param  job   job
    --- @return transition.result
    function(self, job)
        if not job.status
            :set({
                state = self.to.name, -- resolves to `<TO_STATE>`
                message = ("[%s] <TO_STATE> reached"):format(self.to.name),
                progress = 100,
            })
            :send() then
            -- Do not try to execute further transitions, yield to wfx.
            return transition.result.FAIL_YIELD
        end
        return transition.result.COMPLETED
    end
)

See suricatta/server_wfx.lua for examples of such transition execution functions.

New or adapted workflows are supported by appropriately defining/modifying the transition execution functions in suricatta/server_wfx.lua – or taking it as inspiration and creating a new wfx Lua Suricatta module as described in Support for Suricatta Modules in Lua.

Update Activation

The Device Artifact Update (DAU) workflows offer a dedicated activation step in the update steps sequence to decouple artifact installation and activation times so to not, e.g., upon a power-cut, prematurely test-boot into the new firmware after installation until the activation is actually due.

When the activation step is executed, the wfx Lua Suricatta module sends a progress message (see Getting information on running update) upon which a progress client executes or schedules activation measures. For example, the following JSON is sent as the progress message’s .info field on activation of the Job Definition example given above:

{
    "state": "ACTIVATING",
    "progress": 100,
    "message": "firmware:dummy"
}

The progress message’s .status is PROGRESS, see tools/swupdate-progress.c for details on how a progress client can be implemented.

Note: The activation message may be sent multiple times if the update activation is pending, namely on each wfx poll operation and if a new update job is enqueued while the current one is not activated.

Because of the (predefined) firmware label present, the progress client should initiate or schedule a reboot of the device in order to test-boot the new firmware. Also because of the firmware label present, the wfx Lua Suricatta module records the installed update to the bootloader environment. If this label was missing, no such recording would’ve been made which is suitable for, e.g., configuration or application updates.

In order for the this mechanism to work, SWUpdate must not record the update to the bootloader environment after it has installed it or, in case of configuration or application updates, must not touch the bootloader environment at all (see the Sections Update Transaction and Status Marker and bootloader in SWUpdate: syntax and tags with the default parser).

Hence, for firmware updates requiring a test-boot into the new firmware, the following properties should be set in the .swu file’s sw-description:

software =
{
    bootloader_transaction_marker = true;
    bootloader_state_marker = false;
    ...

For configuration or application updates, the following properties apply:

software =
{
    bootloader_transaction_marker = false;
    bootloader_state_marker = false;
    ...

Support for general purpose HTTP server

This is a very simple backend that uses standard HTTP response codes to signal if an update is available. There are closed source backends implementing this interface, but because the interface is very simple interface, this server type is also suitable for implementing an own backend server. For inspiration, there’s a simple (mock) server implementation available in examples/suricatta/server_general.py.

The API consists of a GET with Query parameters to inform the server about the installed version. The query string has the format:

http(s)://<base URL>?param1=val1&param2=value2...

As examples for parameters, the device can send its serial number, MAC address and the running version of the software. It is duty of the backend to interpret this - SWUpdate just takes them from the “identify” section of the configuration file and encodes the URL.

The server answers with the following return codes:

HTTP Code

Text

Description

302

Found

A new software is available at URL in the Location header

400

Bad Request

Some query parameters are missing or in wrong format

403

Forbidden

Client certificate not valid

404

Not found

No update is available for this device

503

Unavailable

An update is available but server can’t handle another update process now.

Server’s answer can contain the following headers:

Header’s name

Codes

Description

Retry-after

503

Contains a number which tells the device how long to wait until ask the next time for updates. (Seconds)

Content-MD5

302

Contains the checksum of the update file which is available under the url of location header

Location

302

URL where the update file can be downloaded.

The device can send logging data to the server. Any information is transmitted in a HTTP PUT request with the data as plain string in the message body. The Content-Type Header need to be set to text/plain.

The URL for the logging can be set as separate URL in the configuration file or via –logurl command line parameter:

The device sends data in a CSV format (Comma Separated Values). The format is:

value1,value2,...

The format can be specified in the configuration file. A format For each event can be set. The supported events are:

Event

Description

check

dummy. It could send an event each time the server is polled.

started

A new software is found and SWUpdate starts to install it

success

A new software was successfully installed

fail

Failure by installing the new software

The general server has an own section inside the configuration file. As example:

gservice =
{
        url             = ....;
        logurl          = ;
        logevent : (
                {event = "check"; format="#2,date,fw,hw,sp"},
                {event = "started"; format="#12,date,fw,hw,sp"},
                {event = "success"; format="#13,date,fw,hw,sp"},
                {event = "fail"; format="#14,date,fw,hw,sp"}
        );
}

date is a special field and it is interpreted as localtime in RFC 2822 format. Each Comma Separated field is looked up inside the identify section in the configuration file, and if a match is found the substitution occurs. In case of no match, the field is sent as it is. For example, if the identify section has the following values:

identify : (
        { name = "sp"; value = "333"; },
        { name = "hw"; value = "ipse"; },
        { name = "fw"; value = "1.0"; }
);

with the events set as above, the formatted text in case of “success” will be:

Formatted log: #13,Mon, 17 Sep 2018 10:55:18 CEST,1.0,ipse,333

Support for Suricatta Modules in Lua

The server_lua.c C-to-Lua bridge enables writing suricatta modules in Lua. It provides the infrastructure in terms of the interface to SWUpdate “core” to the Lua realm, enabling the “business logic” such as handling update flows and communicating with backend server APIs to be modeled in Lua. To the Lua realm, the server_lua.c C-to-Lua bridge provides the same functionality as the other suricatta modules written in C have, realizing a separation of means and control. Effectively, it lifts the interface outlined in Section The Suricatta Interface to the Lua realm.

As an example server implementation, see examples/suricatta/server_general.py for a simple (mock) server of a backend that’s modeled after the “General Purpose HTTP Server” (cf. Section Support for general purpose HTTP server). The matching Lua suricatta module is found in examples/suricatta/swupdate_suricatta.lua. Place it in Lua’s path so that a require("swupdate_suricatta") can load it or embed it into the SWUpdate binary by enabling CONFIG_EMBEDDED_SURICATTA_LUA and setting CONFIG_EMBEDDED_SURICATTA_LUA_SOURCE accordingly.

The interface specification in terms of a Lua (suricatta) module is found in suricatta/suricatta.lua, reproduced for convenience in Section Lua Suricatta Interface Specification.

suricatta

The suricatta table is the module’s main table housing the exposed functions and definitions via the sub-tables described below. In addition, the main functions suricatta.install() and suricatta.download() as well as the convenience functions suricatta.getversion(), suricatta.sleep(), and suricatta.get_tmpdir() are exposed:

The function suricatta.install(install_channel) installs an update artifact from a remote server or a local file. The install_channel table parameter designates the channel to be used for accessing the artifact plus channel options diverging from the defaults set at channel creation time. For example, an install_channel table may look like this:

{ channel = chn, url = "https://artifacts.io/update.swu" }

where chn is the return value of a call to channel.open(). The other table attributes, like url in this example, are channel options diverging from or omitted while channel creation time, see suricatta.channel. For installing a local file, an install_channel table may look like this:

{ channel = chn, url = "file:///path/to/file.swu" }

The function suricatta.download(download_channel, localpath) just downloads an update artifact. The parameter download_channel is as for suricatta.install(). The parameter localpath designates the output path for the artifact. The suricatta.get_tmpdir() function (see below) is in particular useful for this case to supply a temporary download location as localpath. A just downloaded artifact may be installed later using suricata.install() with an appropriate file:// URL, realizing a deferred installation.

Both, suricatta.install() and suricatta.download() return true, or, in case of error, nil, a suricatta.status value, and a table with messages in case of errors, else an empty table.


The function suricatta.getversion() returns a table with SWUpdate’s version and patchlevel fields. This information can be used to determine API (in-)compatibility of the Lua suricatta module with the SWUpdate version running it.

The function suricatta.sleep(seconds) is a wrapper around SLEEP(3) for, e.g., implementing a REST API call retry mechanism after a number of given seconds have elapsed.

The function suricatta.get_tmpdir() returns the path to SWUpdate’s temporary working directory where, e.g., the suricatta.download() function may place the downloaded artifacts.

suricatta.status

The suricatta.status table exposes the server_op_res_t enum values defined in include/util.h to the Lua realm.

suricatta.notify

The suricatta.notify table provides the usual logging functions to the Lua suricatta module matching their uppercase-named pendants available in the C realm.

One notable exception is suricatta.notify.progress(message) which dispatches the message to the progress interface (see Getting information on running update). Custom progress client implementations listening and acting on custom progress messages can be realized using this function.

All notify functions return nil.

suricatta.pstate

The suricatta.pstate table provides a binding to SWUpdate’s (persistent) state handling functions defined in include/state.h, however, limited to the bootloader environment variable STATE_KEY defined by CONFIG_UPDATE_STATE_BOOTLOADER and defaulting to ustate. In addition, it captures the update_state_t enum values.

The function suricatta.pstate.save(state) requires one of suricatta.pstate’s “enum” values as parameter and returns true, or, in case of error, nil. The function suricatta.pstate.get() returns one of suricatta.pstate’s “enum” values or, in case of error, STATE_ERROR.

suricatta.server

The suricatta.server table provides the sole function suricatta.server.register(function_p, purpose). It registers a Lua function “pointed” to by function_p for the purpose purpose which is defined by suricatta.server’s “enum” values. Those enum values correspond to the functions defined in the interface outlined in the Section on The Suricatta Interface.

In addition to these functions, the two callback functions CALLBACK_PROGRESS and CALLBACK_CHECK_CANCEL can be registered optionally: The former can be used to upload progress information to the server while the latter serves as dwlwrdata function (see include/channel_curl.h) to decide on whether an installation should be aborted while the download phase.

For details on the (callback) functions and their signatures, see the interface specification suricatta/suricatta.lua and the documented example Lua suricatta module found in examples/suricatta/swupdate_suricatta.lua.

The suricatta.server.register() function returns true, or, in case of error, nil.

suricatta.channel

The suricatta.channel table captures channel handling for suricatta Lua modules. The single function suricatta.channel.open(options) creates and opens a channel to a server. Its single parameter options is a table specifying the channel’s default options such as proxy, retries, usessl, strictssl, or headers_to_send. For convenience, options that may change per request such as url, content-type, or headers_to_send may be set as defaults on channel creation time while being selectively overruled on a per request basis. The channel options currently supported to be set are listed in the suricatta.channel.options table. In essence, the options parameter table is the Lua table equivalent of include/channel_curl.h’s channel_data_t.

The suricatta.channel.open(options) function returns a channel table which is either passed to the suricatta.install() and suricatta.download() functions or used directly for communication with a server. More specifically, it has the three functions

  • get(options) for retrieving information from the server,

  • put(options) for sending information to the server, and

  • close() for closing the channel.

The get() and put() functions’ single parameter options is a per-request channel option table as described above.

The functions get() and put() return true, or, in case of error, nil, a suricatta.status value, and an operation result table. The latter contains the fields:

  • http_response_code carrying the HTTP error code,

  • format as one of suricatta.channel.content’s options,

  • raw_reply if options contained format = suricatta.channel.content.RAW,

  • json_reply if options contained format = suricatta.channel.content.JSON, and

  • the HTTP headers received in the received_headers table, if any.

The suricatta.channel.content “enum” table defines the “format”, i.e., the response body content type and whether to parse it or not:

  • NONE means the response body is discarded.

  • RAW means the raw server’s reply is available as raw_reply.

  • JSON means the server’s JSON reply is parsed into a Lua table and available as json_reply.

The suricatta.channel.method “enum” table defines the HTTP method to use for a request issued with the put(options) function, i.e., POST, PATCH, or PUT as specified in the options parameter table via the method attribute. In addition to the HTTP method, the request body’s content is set with the request_body attribute in the options parameter table.

As a contrived example, consider the following call to a channel’s put() function

...
local res, _, data = channel.put({
        url          = string.format("%s/%s", base_url, device_id),
        content_type = "application/json",
        method       = suricatta.channel.method.PATCH,
        format       = suricatta.channel.content.NONE,
        request_body = "{ ... }"
    })
...

that issues a HTTP PATCH to some URL with a JSON content without having interest in the response body.

More examples of how to use a channel can be found in the example suricatta Lua module examples/suricatta/swupdate_suricatta.lua.

suricatta.bootloader

The suricatta.bootloader table exposes SWUpdate’s bootloader environment modification functions to suricatta Lua modules.

The enum-like table suricatta.bootloader.bootloaders holds the bootloaders SWUpdate supports, i.e.

suricatta.bootloader.bootloaders = {
    EBG   = "ebg",
    NONE  = "none",
    GRUB  = "grub",
    UBOOT = "uboot",
},

The function suricatta.bootloader.get() returns the currently selected bootloader in terms of a suricatta.bootloader.bootloaders field value.

The function suricatta.bootloader.is(name) takes one of suricatta.bootloader.bootloaders’s field values as name and returns true if it is the currently selected bootloader, false otherwise.

The functions in the suricatta.bootloader.env table interact with the currently selected bootloader’s environment:

The function suricatta.bootloader.env.get(variable) retrieves the value associated to variable from the bootloader’s environment.

The function suricatta.bootloader.env.set(variable, value) sets the bootloader environment’s key variable to value.

The function suricatta.bootloader.env.unset(variable) deletes the bootloader environment’s key variable.

The function suricatta.bootloader.env.apply(filename) applies all key=value lines of a local file filename to the currently selected bootloader’s environment.

Lua Suricatta Interface Specification

  1--[[
  2
  3    SWUpdate Suricatta Lua Module.
  4
  5    Interface specification for the Lua module provided by the
  6    "Suricatta Lua module" suricatta "server" (suricatta/server_lua.c).
  7
  8    Author: Christian Storm <christian.storm@siemens.com>
  9    Copyright (C) 2022, Siemens AG
 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 max line length
 18-- luacheck: no unused args
 19
 20-- Note: Definitions prefixed with `---` are not part of the SWUpdate functionality
 21-- exposed to the Lua realm but instead are `typedef`-alikes returned by a function
 22-- of the SWUpdate Suricatta Lua Module. Nonetheless, they are in the suricatta
 23-- "namespace" for avoiding name clashes and, not least, convenience.
 24
 25--- SWUpdate Suricatta Binding.
 26--
 27--- @class suricatta
 28local suricatta = {}
 29
 30--- @enum suricatta.status
 31--- Lua equivalent of `server_op_res_t` enum as in `include/util.h`.
 32suricatta.status = {
 33    OK                  = 0,
 34    EERR                = 1,
 35    EBADMSG             = 2,
 36    EINIT               = 3,
 37    EACCES              = 4,
 38    EAGAIN              = 5,
 39    UPDATE_AVAILABLE    = 6,
 40    NO_UPDATE_AVAILABLE = 7,
 41    UPDATE_CANCELED     = 8,
 42    ID_REQUESTED        = 9,
 43}
 44
 45
 46--- SWUpdate notify function bindings.
 47--
 48-- Translates to `notify(string.format(message, ...))`,
 49-- @see `corelib/lua_interface.c`
 50--
 51--- @class suricatta.notify
 52suricatta.notify = {
 53    --- @type fun(message: string, ...: any)
 54    error    = function(message, ...) end,
 55    --- @type fun(message: string, ...: any)
 56    trace    = function(message, ...) end,
 57    --- @type fun(message: string, ...: any)
 58    debug    = function(message, ...) end,
 59    --- @type fun(message: string, ...: any)
 60    info     = function(message, ...) end,
 61    --- @type fun(message: string, ...: any)
 62    warn     = function(message, ...) end,
 63    --- @type fun(message: string, ...: any)
 64    progress = function(message, ...) end,
 65}
 66
 67
 68--- SWUpdate's bootloader interface as in `include/bootloader.h`.
 69--
 70--- @class suricatta.bootloader
 71suricatta.bootloader = {
 72    --- @enum suricatta.bootloader.bootloaders
 73    --- Bootloaders supported by SWUpdate.
 74    bootloaders = {
 75        EBG   = "ebg",
 76        NONE  = "none",
 77        GRUB  = "grub",
 78        UBOOT = "uboot",
 79    },
 80}
 81
 82--- Get currently set bootloader's name.
 83--
 84--- @return suricatta.bootloader.bootloaders | nil  # Name of currently set bootloader
 85suricatta.bootloader.get = function() end
 86
 87--- Test whether bootloader `name` is currently set.
 88--
 89--- @param  name     suricatta.bootloader.bootloaders  Name of bootloader to test for being currently selected
 90--- @return boolean                                    # True if `name` is currently set bootloader, false otherwise
 91suricatta.bootloader.is = function(name) end
 92
 93--- Operations on the currently set bootloader's environment.
 94--
 95--- @class suricatta.bootloader.env
 96suricatta.bootloader.env = {}
 97
 98--- Get value of a bootloader environment variable.
 99--
100--- @param  variable  string  Name of the bootloader environment variable to get value for
101--- @return string | nil      # Value of the bootloader environment variable or nil if non-existent
102suricatta.bootloader.env.get = function(variable) end
103
104--- Set value of a bootloader environment variable.
105--
106--- @param  variable  string  Name of the bootloader environment variable to set
107--- @param  value     string  Value to set the bootloader environment variable `variable` to
108--- @return boolean | nil     # True on success, nil on error
109suricatta.bootloader.env.set = function(variable, value) end
110
111--- Drop a bootloader environment variable.
112--
113--- @param  variable  string  Name of the bootloader environment variable to drop
114--- @return boolean | nil     # True on success, nil on error
115suricatta.bootloader.env.unset = function(variable) end
116
117--- Set multiple bootloader environment variables from local file.
118--
119--- @param  filename  string  Path to local file in format `<variable>=<value>`
120--- @return boolean | nil     # True on success, nil on error
121suricatta.bootloader.env.apply = function(filename) end
122
123
124--- SWUpdate's persistent state IDs as in `include/state.h` and reverse-lookup.
125--
126--- @class suricatta.pstate
127suricatta.pstate = {
128    OK            = string.byte('0'), [string.byte('0')] = "OK",
129    INSTALLED     = string.byte('1'), [string.byte('1')] = "INSTALLED",
130    TESTING       = string.byte('2'), [string.byte('2')] = "TESTING",
131    FAILED        = string.byte('3'), [string.byte('3')] = "FAILED",
132    NOT_AVAILABLE = string.byte('4'), [string.byte('4')] = "NOT_AVAILABLE",
133    ERROR         = string.byte('5'), [string.byte('5')] = "ERROR",
134    WAIT          = string.byte('6'), [string.byte('6')] = "WAIT",
135    IN_PROGRESS   = string.byte('7'), [string.byte('7')] = "IN_PROGRESS",
136}
137
138--- Get the current stored persistent state.
139--
140--- @return number   # Persistent state ID number, suricatta.pstate.ERROR if unsuccessful
141suricatta.pstate.get = function() end
142
143--- Save persistent state information.
144--
145--- @param  state  number  Persistent state ID number
146--- @return boolean | nil  # True on success, nil on error
147suricatta.pstate.save = function(state) end
148
149
150
151--- Function registry IDs for Lua suricatta functions.
152--
153--- @class suricatta.server
154suricatta.server = {
155    HAS_PENDING_ACTION    = 0,
156    INSTALL_UPDATE        = 1,
157    SEND_TARGET_DATA      = 2,
158    GET_POLLING_INTERVAL  = 3,
159    SERVER_START          = 4,
160    SERVER_STOP           = 5,
161    IPC                   = 6,
162    PRINT_HELP            = 7,
163    CALLBACK_PROGRESS     = 8,
164    CALLBACK_CHECK_CANCEL = 9,
165}
166
167--- Register a Lua function as Suricatta interface implementation.
168--
169--- @param  function_p  function   Function to register for `purpose`
170--- @param  purpose     number     Suricatta interface function implemented (one of suricatta.server's enums)
171--- @return boolean                # Whether operation was successful or not
172suricatta.server.register = function(function_p, purpose) end
173
174
175--- Channel result table.
176--
177-- Result in return of a channel operation.
178--
179--- @class suricatta.channel_operation_result
180--- @field http_response_code  number
181--- @field format              suricatta.channel.content
182--- @field json_reply          table | nil   Table if `format` was `suricatta.channel.content.JSON`
183--- @field raw_reply           string | nil  Table if `format` was `suricatta.channel.content.RAW`
184--- @field received_headers    table<string, string> | nil
185
186
187--- Options and methods on the opened channel.
188--
189-- Table as returned by `suricatta.channel.open()`.
190--
191--- @class suricatta.open_channel
192--- @field options  suricatta.channel.options                                                                               Channel creation-time set options as in `include/channel_curl.h`.
193--- @field get      fun(options: suricatta.channel.options): boolean, suricatta.status, suricatta.channel_operation_result  Channel get operation
194--- @field put      fun(options: suricatta.channel.options): boolean, suricatta.status, suricatta.channel_operation_result  Channel put operation
195--- @field close    fun()                                                                                                   Channel close operation
196
197--- @class suricatta.channel
198suricatta.channel = {
199
200    -- Lua-alike of proxy environment variable usage marker as in `include/channel_curl.h`.
201    -- An empty `proxy` string means to use proxy environment variables.
202    -- @type string
203    USE_PROXY_ENV = "",
204
205    --- @enum suricatta.channel.content
206    --- Content type passed over the channel as in `include/channel_curl.h`.
207    content = {
208        NONE = 0,
209        JSON = 1,
210        RAW  = 2,
211    },
212
213    --- @enum suricatta.channel.method
214    --- Transfer method to use over channel as in `include/channel_curl.h`.
215    method = {
216        GET   = 0,
217        POST  = 1,
218        PUT   = 2,
219        PATCH = 3,
220    },
221
222    --- Channel options as in `include/channel_curl.h`.
223    --
224    --- @class suricatta.channel.options
225    --- @field url                 string | nil   `CURLOPT_URL` - URL for this transfer
226    --- @field cached_file         string | nil   Resume download from cached file at path
227    --- @field auth                string | nil   `CURLOPT_USERPWD` - user name and password to use in authentication
228    --- @field request_body        string | nil   Data to send to server for `PUT` and `POST`
229    --- @field iface               string | nil   `CURLOPT_INTERFACE` - source interface for outgoing traffic
230    --- @field dry_run             boolean | nil  `swupdate_request`'s dry_run field as in `include/network_ipc.h`
231    --- @field cafile              string | nil   `CURLOPT_CAINFO` - path to Certificate Authority (CA) bundle
232    --- @field sslkey              string | nil   `CURLOPT_SSLKEY` - private key file for TLS and SSL client cert
233    --- @field sslcert             string | nil   `CURLOPT_SSLCERT` - SSL client certificate
234    --- @field ciphers             string | nil   `CURLOPT_SSL_CIPHER_LIST` - ciphers to use for TLS
235    --- @field proxy               string | nil   `CURLOPT_PROXY` - proxy to use
236    --- @field info                string | nil   `swupdate_request`'s info field as in `include/network_ipc.h`
237    --- @field auth_token          string | nil   String appended to Header
238    --- @field content_type        string | nil   `Content-Type:` and `Accept:` appended to Header
239    --- @field retry_sleep         number | nil   Time to wait prior to retry and resume a download
240    --- @field method              suricatta.channel.method | nil  Channel transfer method to use
241    --- @field retries             number | nil   Maximal download attempt count
242    --- @field low_speed_timeout   number | nil   `CURLOPT_LOW_SPEED_TIME` - low speed limit time period
243    --- @field connection_timeout  number | nil   `CURLOPT_CONNECTTIMEOUT` - timeout for the connect phase
244    --- @field format              suricatta.channel.content | nil  Content type passed over the channel
245    --- @field debug               boolean | nil  Set channel debug logging
246    --- @field usessl              boolean | nil  Enable SSL hash sum calculation
247    --- @field strictssl           boolean | nil  `CURLOPT_SSL_VERIFYHOST` + `CURLOPT_SSL_VERIFYPEER`
248    --- @field nocheckanswer       boolean | nil  Whether the reply is interpreted/logged and tried to be parsed
249    --- @field nofollow            boolean | nil  `CURLOPT_FOLLOWLOCATION` - follow HTTP 3xx redirects
250    --- @field max_download_speed  string | nil   `CURLOPT_MAX_RECV_SPEED_LARGE` - rate limit data download speed
251    --- @field headers_to_send     table<string, string> | nil  Header to send
252    options = {},
253
254    --- Open a new channel.
255    --
256    --- @param  options  suricatta.channel.options  Channel default options overridable per operation
257    --- @return boolean                             # Whether operation was successful or not
258    --- @return suricatta.open_channel              # Options of and operations on the opened channel
259    open = function(options) end,
260}
261
262
263--- Channel and Options Table to use for an operation.
264--
265-- Channel to use for the download / installation operation as returned by `suricatta.channel.open()`
266-- plus channel options overriding the defaults per operation (@see suricatta.channel.options)
267-- and specific options to the download / installation operation, e.g., `drain_messages`.
268--
269--- @class suricatta.operation_channel
270--- @field channel          suricatta.open_channel           Channel table as returned by `suricatta.channel.open()`
271--- @field drain_messages   boolean  | nil                   Whether to flush all progress messages or only those while in-flight operation (default)
272--- @field ∈                suricatta.channel.options | nil  Channel options to override for this operation
273
274--- Install an update artifact from remote server or local file.
275--
276-- If the protocol specified in Table `install_channel`'s `url` field is `file://`,
277-- a local update artifact file is installed. If it is, e.g., `https://`, the
278-- update artifact is downloaded *and* installed.
279-- Note that this file is to be deleted, if applicable, from the Lua realm.
280--
281--- @see suricatta.download
282--- @param  install_channel  suricatta.operation_channel  Channel to use for the download+installation operation
283--- @return boolean                                       # Whether operation was successful or not
284--- @return suricatta.status                              # Suricatta return code
285--- @return table<number, string>                         # Error messages, if any
286suricatta.install = function(install_channel) end
287
288--- Download an update artifact from remote server.
289--
290-- `suricatta.download()` just downloads an update artifact from the remote server
291-- without installing it. For later installation, call `suricatta.install()` with
292-- an appropriate `install_channel` Table's `url` field.
293--
294--- @see suricatta.install
295--- @param  download_channel  suricatta.operation_channel  Channel to use for the download operation
296--- @param  localpath         string                       Path where to store the downloaded artifact to
297--- @return boolean                                        # Whether operation was successful or not
298--- @return suricatta.status                               # Suricatta return code
299--- @return table<number, string>                          # Error messages, if any
300suricatta.download = function(download_channel, localpath) end
301
302
303--- Sleep for a number of seconds.
304--
305-- Call `SLEEP(3)` via C realm.
306--
307--- @param seconds number  # Number of seconds to sleep
308suricatta.sleep = function(seconds) end
309
310
311--- Get TMPDIR from SWUpdate.
312--
313-- @see `core/util.c` :: get_tmpdir()
314--
315--- @return string  # TMPDIR path
316suricatta.get_tmpdir = function() end
317
318
319--- SWUpdate version information.
320--- @class suricatta.version
321--- @field [1]         number  SWUpdate's version
322--- @field [2]         number  SWUpdate's patch level
323--- @field version     number  SWUpdate's version
324--- @field patchlevel  number  SWUpdate's patch level
325
326--- Get SWUpdate version.
327--
328--- @return suricatta.version  # Table with `version` and `patchlevel` fields
329suricatta.getversion = function() end
330
331
332--- SWUpdate IPC types and definitions.
333--
334--- @class suricatta.ipc
335suricatta.ipc = {}
336
337--- @enum suricatta.ipc.sourcetype
338--- Lua equivalent of `sourcetype` as in `include/swupdate_status.h`.
339suricatta.ipc.sourcetype = {
340    SOURCE_UNKNOWN           = 0,
341    SOURCE_WEBSERVER         = 1,
342    SOURCE_SURICATTA         = 2,
343    SOURCE_DOWNLOADER        = 3,
344    SOURCE_LOCAL             = 4,
345    SOURCE_CHUNKS_DOWNLOADER = 5
346}
347
348--- @enum suricatta.ipc.RECOVERY_STATUS
349--- Lua equivalent of `RECOVERY_STATUS` as in `include/swupdate_status.h`.
350suricatta.ipc.RECOVERY_STATUS = {
351    IDLE       = 0,
352    START      = 1,
353    RUN        = 2,
354    SUCCESS    = 3,
355    FAILURE    = 4,
356    DOWNLOAD   = 5,
357    DONE       = 6,
358    SUBPROCESS = 7,
359    PROGRESS   = 8
360}
361
362--- @enum suricatta.ipc.progress_cause
363--- Lua equivalent of `progress_cause_t` as in `include/progress_ipc.h`.
364suricatta.ipc.progress_cause = {
365    CAUSE_NONE        = 0,
366    CAUSE_REBOOT_MODE = 1,
367}
368
369--- Lua-alike of `progress_msg` as in `include/progress_ipc.h`.
370--
371--- @class suricatta.ipc.progress_msg
372--- @field magic        number                         SWUpdate IPC magic number
373--- @field status       suricatta.ipc.RECOVERY_STATUS  Update status
374--- @field dwl_percent  number                         Percent of downloaded data
375--- @field nsteps       number                         Total steps count
376--- @field cur_step     number                         Current step
377--- @field cur_percent  number                         Percent in current step
378--- @field cur_image    string                         Name of the current image to be installed (max: 256 chars)
379--- @field hnd_name     string                         Name of the running handler (max: 64 chars)
380--- @field source       suricatta.ipc.sourcetype       The source that has triggered the update
381--- @field info         string                         Additional information about the installation (max: 2048 chars)
382--- @field jsoninfo     table                          If `info` is JSON, according Lua Table
383
384--- Lua enum of IPC commands as in `include/network_ipc.h`.
385--
386-- `CMD_ENABLE` is not passed through and hence not in `ipc_commands`
387-- as it's handled directly in `suricatta/suricatta.c`.
388--
389--- @type  {[string]: number}
390--- @class suricatta.ipc.ipc_commands
391--- @field ACTIVATION  number  0
392--- @field CONFIG      number  1
393--- @field GET_STATUS  number  3
394
395--- Lua-alike of `ipc_message` as in `include/network_ipc.h`.
396--
397-- Note: Some members are deliberately not passed through to the Lua
398-- realm such as `ipc_message.data.len` since that's transparently
399-- handled by the C-to-Lua bridge.
400-- Note: This is not a direct equivalent but rather a "sensible" selection
401-- as, e.g., the `json` field is not present in `struct ipc_message`.
402--
403--- @class suricatta.ipc.ipc_message
404--- @field magic     number                      SWUpdate IPC magic number
405--- @field commands  suricatta.ipc.ipc_commands  IPC commands
406--- @field cmd       number                      Command number, one of `ipc_commands`'s values
407--- @field msg       string                      String data sent via IPC
408--- @field json      table                       If `msg` is JSON, JSON as Lua Table
409
410
411return suricatta