Skip to content

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

Usage in golang

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)
	}
	...

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")
	}
	...