| // 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 |
| } |