endSessionDialog: Support eos-updater [DRAFT]

eos-updater is used in Endless OS and GNOME OS.

Much more invasive changes will be needed since the eos-updater state
machine looks very different to the packagekit state machine. The update
is staged in the background; once staged, the next clean
shutdown/startup or reboot will boot straight into the new version.
This commit is contained in:
Will Thompson 2022-01-26 10:43:13 +00:00
parent 0812722a5b
commit 142a0f406b
3 changed files with 327 additions and 0 deletions

View file

@ -0,0 +1,292 @@
<!DOCTYPE node PUBLIC
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
<node>
<!--
com.endlessm.Updater:
@short_description: Endless OS updater control interface
Interface to query the state of the OS updater, and to control it: checking
for, downloading and applying updates.
The updater moves through a series of states (see the `State` property and
`StateChanged` signal), with state transitions triggered by the appropriate
method call, or by an error in an operation. It will return a
`com.endlessm.Updater.Error.WrongState` error if you attempt to perform an
invalid state transition.
-->
<interface name="com.endlessm.Updater">
<!--
Poll:
Check for updates. This may be performed from the `Ready`,
`UpdateAvailable`, `UpdateReady` or `Error` states. It will immediately
transition to the `Polling` state. If an update is then successfully
found, it will transition to the `UpdateAvailable` state. If no update is
available, it will transition to the `Ready` state. If there is an error
checking for an update, it will transition to the `Error` state.
If an update is found, its details will be reported through the updaters
properties once the updater reaches the `UpdateAvailable` state. In
particular, through the `UpdateID`, `UpdateRefspec`, `UpdateLabel` and
`UpdateMessage` properties.
-->
<method name="Poll"></method>
<!--
PollVolume:
@path: Absolute path to the root directory of a volume to check.
Like `Poll`, except this polls a specific removable volume (such as a USB
stick), rather than the internet.
If a `.ostree/repo` directory is available beneath @path, and it contains
a valid OSTree repository, that repository will be checked for updates.
If no such directory exists, the updater will transition to state `Ready`
as no update is available.
-->
<method name="PollVolume">
<arg name="path" type="s" direction="in"/>
</method>
<!--
Fetch:
Download an update. This may be performed from the `UpdateAvailable` state
only. It will immediately transition to the `Fetching` state. If the
update is successfully downloaded, it will transition to the `UpdateReady`
state; if there was an error it will transition to `Error`.
Download progress will be reported through the updaters properties; in
particular, `DownloadedBytes`.
-->
<method name="Fetch"></method>
<!--
FetchFull:
@options: Potentially empty dictionary of options.
Like `Fetch`, except options may be provided to affect its behaviour.
Currently, the following options are supported (unsupported options are
ignored):
* `force` (type: `b`): If true, force the download without scheduling
it through the systems metered data scheduler. Typically, this would
be true in response to an explicit user action, and false otherwise.
* `scheduling-timeout-seconds` (type: `u`): Number of seconds to wait
for permission to download from the systems metered data scheduler,
before returning a `com.endlessm.Updater.Error.MeteredConnection`
error and cancelling the download. Pass zero to disable the timeout.
-->
<method name="FetchFull">
<arg name="options" type="a{sv}" direction="in"/>
</method>
<!--
Apply:
Apply a downloaded update so that its available to boot into on the next
boot. This may be performed from the `UpdateReady` state only. It will
immediately transition to the `Applying` state. If the update is
successfully applied, it will transition to the `UpdateApplied` state; if
there was an error it will transition to `Error`.
-->
<method name="Apply"></method>
<!--
Cancel:
Cancel the ongoing poll, fetch or apply operation. This may be performed
from the `Polling`, `Fetching` or `ApplyingUpdate` states. It will cancel
the operation then transition to the `Error` state, with the error
`com.endlessm.Updater.Error.Cancelled`.
-->
<method name="Cancel"></method>
<!--
State:
Current state of the updater. This will be one of the following:
* `0` (`None`): No state.
* `1` (`Ready`): Ready to perform an action.
* `2` (`Error`): An error occurred. See the `ErrorName` and
`ErrorMessage` properties for details.
* `3` (`Polling`): Checking for updates.
* `4` (`UpdateAvailable`): An update is available. See the `UpdateID`,
`UpdateRefspec`, `UpdateLabel` and `UpdateMessage` properties for
details.
* `5` (`Fetching`): Downloading an update. See the `DownloadedBytes`
property for progress updates.
* `6` (`UpdateReady`): Update downloaded and ready to apply.
* `7` (`ApplyingUpdate`): Applying an update.
* `8` (`UpdateApplied`): Update applied and ready to reboot into.
State changes are notified using the `StateChanged` signal.
-->
<property name="State" type="u" access="read"/>
<!--
UpdateID:
Checksum of the OSTree commit available as an update, or the empty string
if no update is available.
-->
<property name="UpdateID" type="s" access="read"/>
<!--
UpdateRefspec:
Refspec (remote name and branch name) of the OSTree commit available as an
update, or the empty string if no update is available.
-->
<property name="UpdateRefspec" type="s" access="read"/>
<!--
OriginalRefspec:
Refspec (remote name and branch name) of the currently booted OSTree
commit.
-->
<property name="OriginalRefspec" type="s" access="read"/>
<!--
CurrentID:
Checksum of the currently booted OSTree commit.
-->
<property name="CurrentID" type="s" access="read"/>
<!--
UpdateLabel:
Subject of the OSTree commit available as an update, or the empty string
if its not set or if no update is available. This is the title of the
update.
-->
<property name="UpdateLabel" type="s" access="read"/>
<!--
UpdateMessage:
Description body of the OSTree commit available as an update, or the empty
string if its not set or if no update is available. This is the
description of the update.
-->
<property name="UpdateMessage" type="s" access="read"/>
<!--
Version:
Version number of the OSTree commit available as an update, or the empty
string if its not set or if no update is available.
-->
<property name="Version" type="s" access="read"/>
<!--
DownloadSize:
Size (in bytes) of the update when downloaded, or `-1` if an update is
available but its download size is unknown. `0` if no update is available.
-->
<property name="DownloadSize" type="x" access="read"/>
<!--
DownloadedBytes:
Number of bytes of the update which have already been downloaded. This
will be `0` before a download starts, and could be `-1` if the
`DownloadSize` is unknown.
-->
<property name="DownloadedBytes" type="x" access="read"/>
<!--
UnpackedSize:
Size (in bytes) of the update when unpacked, or `-1` if an update is
available but its unpacked size is unknown. `0` if no update is available.
-->
<property name="UnpackedSize" type="x" access="read"/>
<!--
FullDownloadSize:
Version of `DownloadSize` which also includes the sizes of parts of the
update which are already present locally (and hence which dont need to
be downloaded again).
-->
<property name="FullDownloadSize" type="x" access="read"/>
<!--
FullUnpackedSize:
Version of `UnpackedSize` which also includes the sizes of parts of the
update which are already unpacked locally (and hence which wont occupy
further disk space once the update is applied).
-->
<property name="FullUnpackedSize" type="x" access="read"/>
<!--
ErrorCode:
Error code of the current error, or `0` if no error has been reported.
This is in an unspecified error domain, and hence is useless.
Deprecated: Use `ErrorName` instead.
-->
<property name="ErrorCode" type="u" access="read">
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
</property>
<!--
ErrorName:
A fully-qualified D-Bus error name, as might be returned from a D-Bus
method.
This is the empty string if no error has been reported.
Known errors include:
* `com.endlessm.Updater.Error.WrongState`: Method was called in a state
which doesnt support that method.
* `com.endlessm.Updater.Error.LiveBoot`: The updater cannot be used
because the current system is a live boot.
* `com.endlessm.Updater.Error.WrongConfiguration`: A configuration file
contains an error.
* `com.endlessm.Updater.Error.NotOstreeSystem`: The updater cannot be
used because the current system is not booted from an OSTree commit.
* `com.endlessm.Updater.Error.Fetching`: Error when downloading an
update.
* `com.endlessm.Updater.Error.MalformedAutoinstallSpec`: An autoinstall
specification in the pending update contains an error.
* `com.endlessm.Updater.Error.UnknownEntryInAutoinstallSpec`: An
autoinstall specification in the pending update contains an unknown
entry.
* `com.endlessm.Updater.Error.FlatpakRemoteConflict`: An autoinstall
specification in the pending update contains a remote name which
doesnt match the systems configuration.
* `com.endlessm.Updater.Error.MeteredConnection`: A fetch operation timed
out while waiting for permission to download.
-->
<property name="ErrorName" type="s" access="read"/>
<!--
ErrorMessage:
A human-readable (but unlocalised) error message, or the empty string if
no error has been reported.
-->
<property name="ErrorMessage" type="s" access="read"/>
<!--
StateChanged:
@state: The new state.
Signal notifying of a change in the `State` property.
-->
<signal name="StateChanged">
<arg type="u" name="state"/>
</signal>
</interface>
</node>

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gnome/shell/dbus-interfaces">
<file preprocess="xml-stripblanks">com.endlessm.Updater.xml</file>
<file preprocess="xml-stripblanks">net.hadess.PowerProfiles.xml</file>
<file preprocess="xml-stripblanks">net.hadess.SensorProxy.xml</file>
<file preprocess="xml-stripblanks">net.reactivated.Fprint.Device.xml</file>

View file

@ -159,6 +159,9 @@ const LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface);
const PkOfflineIface = loadInterfaceXML('org.freedesktop.PackageKit.Offline');
const PkOfflineProxy = Gio.DBusProxy.makeProxyWrapper(PkOfflineIface);
const EosUpdaterIface = loadInterfaceXML('com.endlessm.Updater');
const EosUpdaterProxy = Gio.DBusProxy.makeProxyWrapper(EosUpdaterIface);
const UPowerIface = loadInterfaceXML('org.freedesktop.UPower.Device');
const UPowerProxy = Gio.DBusProxy.makeProxyWrapper(UPowerIface);
@ -244,6 +247,11 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
'/org/freedesktop/PackageKit',
this._onPkOfflineProxyCreated.bind(this));
this._eosUpdaterProxy = new EosUpdaterProxy(Gio.DBus.system,
'com.endlessm.Updater',
'/com/endlessm/Updater',
this._onEosUpdaterProxyCreated.bind(this));
this._powerProxy = new UPowerProxy(Gio.DBus.system,
'org.freedesktop.UPower',
'/org/freedesktop/UPower/devices/DisplayDevice',
@ -326,6 +334,21 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
}
}
_onEosUpdaterProxyCreated(proxy, error) {
if (error) {
log(error.message);
return;
}
// Creating a D-Bus proxy won't propagate SERVICE_UNKNOWN or NAME_HAS_NO_OWNER
// errors if eos-updater is not available, but the GIO implementation will make
// sure in that case that the proxy's g-name-owner is set to null, so check that.
if (this._eosUpdaterProxy.g_name_owner === null) {
this._eosUpdaterProxy = null;
return;
}
}
_onDestroy() {
this._user.disconnect(this._userLoadedId);
this._user.disconnect(this._userChangedId);
@ -398,6 +421,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
}
// Fall back to regular description
// TODO: dialogContent.description is not a function
if (!description)
description = dialogContent.description(displayTime);
@ -747,6 +771,16 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
this._type = DialogType.UPDATE_RESTART;
else if (this._updateInfo.UpgradeTriggered)
this._type = DialogType.UPGRADE_RESTART;
} else if (this._eosUpdaterProxy && this._type == DialogType.RESTART) {
var x = this._eosUpdaterProxy.get_cached_property("State");
if (x != null && x.get_uint32() === 8) {
this._type = DialogType.UPGRADE_RESTART;
this._updateInfo.PreparedUpgrade = {
// TODO: GNOME OS?
name: "Endless OS",
version: this._eosUpdaterProxy.get_cached_property("Version").get_string(),
};
}
}
this._applications = [];