Updater API
The updater exposes a GRPC based API using a unix socket file /tmp/swupdate.sock
The latest version of the GRPC-Defintion can be found on Github
Non reentrant api
Make sure in the application using a lock or flag that only one API command is executed at the same time.
Usage in golang
Parallel execution
The updater service is running in parallel to the application, which means, that during an update operation the application keeps running and all API calls are non-blocking
Importing
Add the following imports:
import(
"github.com/idastroem/swupdatesvc"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
Connecting
Connect to the unix socket as shown in the following code fragment:
const (
protocol = "unix"
sockAddr = "/tmp/swupdate.sock"
)
...
// connect to update service
rootCtx := context.Background()
mainCtx, mainCxl := context.WithCancel(rootCtx)
var (
credentials = insecure.NewCredentials() // No SSL/TLS
dialer = func(ctx context.Context, addr string) (net.Conn, error) {
var d net.Dialer
return d.DialContext(ctx, protocol, addr)
}
options = []grpc.DialOption{
grpc.WithTransportCredentials(credentials),
grpc.WithBlock(),
grpc.WithContextDialer(dialer),
}
)
updateConnection, err := grpc.Dial(sockAddr, options...)
if err != nil {
panic(err)
}
updateService := swupdatesvc.NewUpdaterClient(updateConnection)
fmt.Println("OK")
...
Checking for updates
The function GetAvailableUpdateBundles takes an updateUrl (e.g. usb:/mnt
) and returns all available update bundles.
These update URLs are currently supported:
usb:<directory-path>
https://<url-of-update-server>
http://<url-of-update-server>
It returns:
- The installed bundle version
- The installed component versions
- All available update bundles.
For each available update bundle the following information is returned:
- fully qualified update URL (e.g.
usb:/mnt/myupdate.zip
) - Bundle version
- List of component versions
- userData
Based on these data the user application decides if an update should be started and which one.
...
result, _ := uc.updateService.GetAvailableUpdateBundles(uc.mainCtx, &swupdatesvc.GetAvailableUpdateBundlesRequest{
UpdateUrl: url,
})
if result.IsError {
return fmt.Errorf("error: '%s'", result.Message)
}
fmt.Printf("Engine Version: %+v\n", result.EngineVersion)
fmt.Printf("Installed Bundle Version: %+v\n", result.CurrentVersion)
for k, v := range result.InstalledComponents {
fmt.Printf("Installed Component: '%s' Version '%s'\n", k, v)
}
for _, v := range result.AvailableBundles {
fmt.Printf("Available Bundle: %+v\n", v)
}
...
USB-Update
There is only one update bundle per USB-Stick available.
Installing an update
The function InstallUpdateBundle takes a fully qualified bundleUrl (retrieved by GetAvailableUpdateBundles) and installs the associated update bundle. During the installation, the progress is reported via a calback function.
...
stream, _ := uc.updateService.InstallUpdateBundle(uc.mainCtx, &swupdatesvc.InstallUpdateBundleRequest{
BundleUrl: bundleUrl,
})
fmt.Println("Starting update...")
fmt.Println()
for {
resp, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
return err
}
if resp != nil {
if resp.Messsage != "" {
fmt.Println(resp.Messsage)
}
if resp.IsError || resp.IsDone {
fmt.Println()
if resp.IsError {
fmt.Println("Update failed.")
return fmt.Errorf("error: '%s'", resp.Messsage)
}
if resp.IsDone {
fmt.Println("Update successful.")
}
break
} else {
fmt.Printf("\rProgress: %d", int(100*resp.Progress))
}
}
}
...
Rollback detection
Using the GetStatus
function the ShouldRollback
flag can be read.
The user application could then display a message, asking the user if she wants to rollback to the current firmware version.
Example:
...
resp, _ := updateService.GetStatus(mainCtx, &swupdatesvc.Empty{})
if resp.ShouldRollback {
fmt.Println("rollback detected")
// ask user what to do
}
...
Committing a rollback
When a rollback has been detected, it can be committed to disk permanently:
...
resp, _ := updateService.Rollback(mainCtx, &swupdatesvc.Empty{})
if resp.IsError {
fmt.Printf("rollback: error : '%s'", resp.Message)
} else {
fmt.Println("rollback success")
}
...