blob: 35e6742b73ca24ce54242f59869ab765e383084f [file] [log] [blame]
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dbus
import (
"fmt"
"policy_manager/policymanagerproto"
"strings"
godbus "github.com/godbus/dbus"
"github.com/golang/protobuf/proto"
)
// Implementation of the UpdateEngineClient interface that interacts with update-engine.
const (
updateEngineObjectName = "org.chromium.UpdateEngine"
updateEngineObjectPath = "/org/chromium/UpdateEngine"
setChannelMethod = "org.chromium.UpdateEngineInterface.SetChannel"
attemptUpdateMethod = "org.chromium.UpdateEngineInterface.AttemptUpdate"
getStatusAdvancedMethod = "org.chromium.UpdateEngineInterface.GetStatusAdvanced"
statusUpdateSignal = "org.chromium.UpdateEngineInterface.StatusUpdate"
dBusAddMatchMethod = "org.freedesktop.DBus.AddMatch"
)
type updateEngineClientImpl struct {
// A shared connection to the system bus.
conn Conn
}
// NewUpdateEngineClient() creates a new UpdateEngineClient.
// This method is Goroutine-safe.
func NewUpdateEngineClient() (UpdateEngineClient, error) {
conn, err := SystemBus()
if err != nil {
return nil, err
}
return &updateEngineClientImpl{conn}, nil
}
// SetChannel Sets the target channel for update.
func (client *updateEngineClientImpl) SetChannel(targetChannel string) error {
call := client.conn.Object(
updateEngineObjectName, updateEngineObjectPath).Call(
setChannelMethod,
0, /* godbus flag */
targetChannel,
false /* isPowerWashAllowed */)
return call.GetError()
}
// AttemptUpdate sends a request to update-engine to check for update.
func (client *updateEngineClientImpl) AttemptUpdate() error {
call := client.conn.Object(
updateEngineObjectName, updateEngineObjectPath).Call(
attemptUpdateMethod,
0, /* godbus flag */
"", /* appVersion */
"" /* omahaUrl */)
return call.GetError()
}
// GetStatus returns the update status
func (client *updateEngineClientImpl) GetStatus() (
*policymanagerproto.StatusResult, error) {
var updateStatusBytes []byte
updateStatus := new(policymanagerproto.StatusResult)
if err := client.conn.Object(updateEngineObjectName, updateEngineObjectPath).Call(
getStatusAdvancedMethod, 0).Store(&updateStatusBytes); err != nil {
return nil, fmt.Errorf("failed to fetch update status: %s", err)
}
if err := proto.Unmarshal(updateStatusBytes, updateStatus); err != nil {
return nil, fmt.Errorf("failed to unmarshal UpdateStatus %s: %s",
updateStatusBytes, updateStatus)
}
return updateStatus, nil
}
// SubscribeStatusUpdate subscribes the status update of update-engine.
// It returns a channel so that any udpate status change of udpate-engine
// will be sent to the channel.
func (client *updateEngineClientImpl) SubscribeStatusUpdate() (
<-chan UEGetStatusResponse, error) {
idx := strings.LastIndex(statusUpdateSignal, ".")
addMatchArgs := fmt.Sprintf(
"type='signal',path='%s',interface='%s',member='%s'",
updateEngineObjectPath,
statusUpdateSignal[:idx],
statusUpdateSignal[idx+1:])
err := client.conn.BusObject().Call(
dBusAddMatchMethod, 0, addMatchArgs).GetError()
if err != nil {
return nil, err
}
ch := make(chan *godbus.Signal, 10)
client.conn.Signal(ch)
out := make(chan UEGetStatusResponse, 10)
go func() {
for signal := range ch {
out <- UEGetStatusResponse{
LastCheckedTime: signal.Body[0].(int64),
Progress: signal.Body[1].(float64),
UpdateStatus: signal.Body[2].(string),
NewVersion: signal.Body[3].(string),
NewSize: signal.Body[4].(int64),
}
}
}()
return out, nil
}