blob: 070ff444c2b24138605d72e518b6cf99870b889d [file] [log] [blame]
package daemon
import (
"amber/pkg"
"bytes"
"fmt"
"log"
"os"
"path/filepath"
"strconv"
"sync/atomic"
"time"
"app/context"
"fidl/fuchsia/amber"
"fidl/fuchsia/sys"
"syscall/zx"
"syscall/zx/zxwait"
"fuchsia.googlesource.com/merkle"
)
var updaterDir = filepath.Join("/pkgfs", "packages", "update")
var updaterBin = filepath.Join("bin", "app")
type SystemUpdateMonitor struct {
halt uint32
daemon *Daemon
checkNow chan struct{}
// if auto is allowed to be reset after instantiation, changes must be made
// in the run loop to avoid a panic
auto bool
}
type ErrNoUpdater string
func (e ErrNoUpdater) Error() string {
return "no system_updater is available on the system"
}
func NewErrNoUpdater() ErrNoUpdater {
return ""
}
func NewSystemUpdateMonitor(d *Daemon, a bool) *SystemUpdateMonitor {
return &SystemUpdateMonitor{daemon: d, halt: 0, checkNow: make(chan struct{}), auto: a}
}
func (upMon *SystemUpdateMonitor) Stop() {
atomic.StoreUint32(&upMon.halt, 1)
}
func (upMon *SystemUpdateMonitor) Check() {
if atomic.LoadUint32(&upMon.halt) == 1 {
return
}
upMon.checkNow <- struct{}{}
}
func (upMon *SystemUpdateMonitor) Start() {
path, err := latestSystemUpdater()
var contentsPath string
merkle := []byte{}
if err != nil {
if _, ok := err.(ErrNoUpdater); !ok {
log.Printf("sys_upd_mon: unexpected error reading updater: %s", err)
return
}
} else {
contentsPath = filepath.Join(path, "meta", "contents")
m, err := statMerkle(contentsPath)
if err != nil {
log.Printf("sys_upd_mon: merkle computation of contents file failed " +
"treating 'update' package as nonexistent")
} else {
merkle = m
}
}
amber, err := connectToUpdateSrvc()
if err != nil {
log.Printf("sys_upd_mon: binding to update service failed: %s", err)
return
}
timerDur := time.Hour
var timer *time.Timer
if upMon.auto {
timer = time.NewTimer(timerDur)
}
for {
if upMon.auto {
select {
case <-timer.C:
timer.Reset(timerDur)
case <-upMon.checkNow:
}
} else {
<-upMon.checkNow
}
if atomic.LoadUint32(&upMon.halt) == 1 {
return
}
updatePkg := &pkg.Package{Name: fmt.Sprintf("/update/%d", 0)}
if err = fetchPackage(updatePkg, amber); err != nil {
log.Printf("sys_upd_mon: unable to fetch package update: %s", err)
continue
}
// if not system updater was available at all before, check for the latest one
if path == "" {
path, err = latestSystemUpdater()
if err != nil {
continue
}
contentsPath = filepath.Join(path, "meta", "contents")
}
newMerkle, err := statMerkle(contentsPath)
if err != nil {
log.Printf("sys_upd_mon: stat of contents file failed: %s", err)
continue
}
if !bytes.Equal(newMerkle, merkle) {
log.Println("System update starting...")
launchDesc := sys.LaunchInfo{Url: "system_updater"}
if err = runProgram(&launchDesc); err != nil {
log.Printf("sys_upd_mon: updater failed to start: %s", err)
}
} else {
log.Println("sys_upd_mon: no newer system version available")
}
merkle = newMerkle
}
}
func runProgram(info *sys.LaunchInfo) error {
context := context.CreateFromStartupInfo()
req, pxy, err := sys.NewLauncherInterfaceRequest()
if err != nil {
return fmt.Errorf("could not make launcher request object: %s", err)
}
context.ConnectToEnvService(req)
defer func() {
c := req.ToChannel()
(&c).Close()
}()
contReq, _, err := sys.NewComponentControllerInterfaceRequest()
if err != nil {
return fmt.Errorf("error creating component controller request: %s", err)
}
err = pxy.CreateComponent(*info, contReq)
if err != nil {
return fmt.Errorf("error starting system updater: %s", err)
}
return nil
}
func connectToUpdateSrvc() (*amber.ControlInterface, error) {
context := context.CreateFromStartupInfo()
req, pxy, err := amber.NewControlInterfaceRequest()
if err != nil {
return nil, fmt.Errorf("error getting control interface: %s", err)
}
context.ConnectToEnvService(req)
return pxy, nil
}
func fetchPackage(p *pkg.Package, amber *amber.ControlInterface) error {
h, err := amber.GetUpdateComplete(p.Name, nil, &p.Merkle)
if err != nil {
return fmt.Errorf("fetch: failed submitting update request: %s", err)
}
defer h.Close()
signals, err := zxwait.Wait(*h.Handle(), zx.SignalChannelPeerClosed|zx.SignalChannelReadable,
zx.TimensecInfinite)
if err != nil {
return fmt.Errorf("fetch: error waiting on result channel: %s", err)
}
buf := make([]byte, 128)
if signals&zx.SignalChannelReadable == zx.SignalChannelReadable {
_, _, err := h.Read(buf, []zx.Handle{}, 0)
if err != nil {
return fmt.Errorf("fetch: error reading channel %s", err)
}
} else {
return fmt.Errorf("fetch: reply channel was not readable")
}
return nil
}
func statMerkle(path string) ([]byte, error) {
f, err := os.Open(path)
if err != nil {
return []byte{}, err
}
defer f.Close()
m := &merkle.Tree{}
if _, err = m.ReadFrom(f); err == nil {
return m.Root(), nil
}
return []byte{}, err
}
func latestSystemUpdater() (string, error) {
dir, err := os.Open(updaterDir)
if err != nil {
if os.IsNotExist(err) {
return "", NewErrNoUpdater()
}
return "", err
}
defer dir.Close()
ents, err := dir.Readdirnames(0)
if len(ents) == 0 {
return "", NewErrNoUpdater()
}
_, verDir, err := GreatestIntStr(ents)
if err != nil {
if err == ErrNoInput {
return "", fmt.Errorf("package has no versions")
}
return "", err
}
return filepath.Join(updaterDir, verDir), nil
}
var ErrNoInput = fmt.Errorf("no inputs supplied")
type ErrNan string
func (e ErrNan) Error() string {
return string(e)
}
func GreatestIntStr(s []string) (int, string, error) {
i := -1
var str string
if len(s) == 0 {
return i, str, ErrNoInput
}
for _, s := range s {
cand, err := strconv.ParseInt(s, 10, 0)
if err != nil {
return -1, "", ErrNan(
fmt.Sprintf("string %q could not be parsed as a number, error: %s", s, err))
}
if int(cand) > i {
i = int(cand)
str = s
}
}
return i, str, nil
}