Client
Client side
Introduction
The client tool is used to calculate the update, download the files, validate them and then set the needed flags for the upgrader to pick them up and apply.
Requirements
- Secure download of the indexes (HTTPS + GPG)
Support for everything described in the GPG spec (revocation list, multiple keyrings, ...)
- Resolution of the best upgrade path based on different policies (total download size, least number of reboots, ...)
- Download and validation of the files
- Flexible implementation to allow different upgrader setups with minimal changes required
- Support for suspend/resume of downloads
Nice to have
- Bandwidth limiting is a nice to have and might be tricky to implement.
Implementation
The current implementation is a command line tool, however it's expected to be turned into a DBus service that the Touch UI can drive.
Step-by-step example for an update
Whenever called the client does the following:
Grab https://server/channels.json and lookup the index for the current channel. If present, also grab the device GPG keyring.
Grab https://server/<channel>/<model>/index.json
- Read the current version number of the device (ubuntu-build file)
- Look for the most recent version available
- Resolve an upgrade path to it, minimizing download size and number of reboots
- Download any file needed up until the next reboot
- Validate all the files
- Write them to the cache partition
- Write the list of updates for the upgrader to use
- Reboot into the upgrader
Those steps don't include all of the specific GPG validation bits required to ensure the authenticity of all files. Those are detailed in the separate GPG wiki page.
Security (e.g. what to download over https/http) is outlined in the server security section.
DBus API
The client will export a DBus API on the system bus which will allow for a u/i in the System Settings to query, begin, cancel, and apply a system update. This service starts via DBus activation and exits automatically after a configurable amount of time (i.e. it does not run forever). It maintains state such that the client can exit and get restarted to continue the update, even across reboots. The client uses the download service to manage all file downloads. Here is the DBus API specification:
CheckUpdateStatus(reset_level)
Asynchronous call to instruct the client to check for an available update. reset_level indicates how the client should check for updates:
0 means Hard reset
If an update is in progress or paused, it will be canceled. In all cases, any previous state will be reset, and a new update will begin. An UpdateAvailableStatus(false, true, "", 0, 0, {}) signal will be issued (arguments mean: no update available yet, an update is downloading, with the last 4 arguments to be ignored).
1 means Keep current state
If the previous update succeeded, a new update will be started and an UpdateAvailableStatus(false, true, "", 0, 0, {}) signal will be issued (arguments mean: no update available yet, an update is downloading, with the last 4 arguments to be ignored).
If an update/download is already in progress, it will continue and an UpdateAvailableStatus(false, true, "", 0, 0, {}) signal will be issued (arguments mean: no update available yet, an update is downloading, with the last 4 arguments to be ignored).
If a download is currently paused, it will resume if possible. An UpdateAvailableStatus(false, downloading, "", 0, 0, {}) signal will be issued (arguments mean: no update available yet, a flag indicating whether the paused download has been resumed, and the last 4 arguments to be ignored).
If a previous update failed, state will not be reset and no new update will be initiated. An UpdateAvailableStatus(false, false, "Download failed", 0, 0, {}) signal will be issued (arguments mean: no update available, not downloading, a human readable error message, with the last 3 arguments to be ignored). After that, an UpdateFailed(buildno, failure_count, "Reason") signal will be issued (arguments mean: the build number that the last update attempted, a consecutive build failure count, and a human readable failure reason).
2 means Restart if in failure state
If the previous update succeeded, a new update will be started, and an UpdateAvailableStatus(false, true, "", 0, 0, {}) signal will be issued (arguments mean: no update available yet, an update is downloading, with the last 4 arguments to be ignored).
If an update/download is already in progress, it will continue, and an UpdateAvailableStatus(false, true, "", 0, 0, {}) signal will be issued (arguments mean: no update available yet, an update is downloading, with the last 4 arguments to be ignored).
If a download is currently paused, it will resume if possible. An UpdateAvailableStatus(false, downloading, "", 0, 0, {}) signal will be issued (arguments mean: no update available yet, a flag indicating whether the paused download has been resumed, and the last 4 arguments to be ignored).
If a previous update failed, reset state and begin a new update. An UpdateAvailableStatus(false, true, "", 0, 0, {}) signal will be issued (arguments mean: no update available yet, an update is downloading, with the last 4 arguments to be ignored).
Note that UpdateAvailableStatus signal is only sent once an update candidate is determined to be available. This may involve downloading some preliminary files from the server (e.g. new keyrings, blacklists, channels.json and index.json files, etc.). If there is no update candidate available no UpdateAvailableStatus signal is sent. XXX Should a signal be sent in this case? If so, what are the values of the arguments? It could be false/false meaning no update is available and we're not downloading, but then what about the following arguments? They should probably be ignored.