blob: 123d88f233bac01e9f85c19d768625836673c138 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package control_server
import (
"fmt"
"log"
"os"
"path/filepath"
"regexp"
"runtime"
"fidl/fuchsia/amber"
"amber/daemon"
"amber/metrics"
"amber/sys_update"
"syscall/zx"
)
type ControlServer struct {
daemon *daemon.Daemon
sysUpdate *sys_update.SystemUpdateMonitor
}
type EventsImpl struct{}
var _ = amber.Events(EventsImpl{})
var merklePat = regexp.MustCompile("^[0-9a-f]{64}$")
func NewControlServer(d *daemon.Daemon, sum *sys_update.SystemUpdateMonitor) *ControlServer {
return &ControlServer{
daemon: d,
sysUpdate: sum,
}
}
func (c *ControlServer) DoTest(in int32) (out string, err error) {
r := fmt.Sprintf("Your number was %d\n", in)
return r, nil
}
func (c *ControlServer) GetProcessState(wr zx.Channel) error {
if err := dumpStacks(wr); err != nil {
log.Printf("control_server: stack dump failed: %s", err)
}
return nil
}
func dumpStacks(ch zx.Channel) error {
defer ch.Close()
buf := make([]byte, 1024)
for {
n := runtime.Stack(buf, true)
if n < len(buf) {
break
}
buf = make([]byte, 2*len(buf))
}
chunks := chunkBuffer(buf, 63*1024)
for _, chunk := range chunks {
err := ch.Write(chunk, []zx.Handle{}, 0)
if err != nil {
return fmt.Errorf("control_server: write to channel failed: %s", err)
}
}
return nil
}
func chunkBuffer(buf []byte, chunkSize int) [][]byte {
if chunkSize < 1 {
panic(fmt.Sprintf("chunk size of %d is invalid, must be greater than 0", chunkSize))
}
chunks := [][]byte{}
for sent := 0; sent < len(buf); {
if len(buf)-sent < chunkSize {
chunkSize = len(buf) - sent
}
chunks = append(chunks, buf[sent:sent+chunkSize])
sent += chunkSize
}
return chunks
}
func (c *ControlServer) AddSrc(cfg amber.SourceConfig) (bool, error) {
if err := c.daemon.AddSource(&cfg); err != nil {
log.Printf("error adding source: %s", err)
return false, nil
}
return true, nil
}
func (c *ControlServer) CheckForSystemUpdate() (bool, error) {
go c.sysUpdate.Check(metrics.InitiatorManual)
return true, nil
}
func (c *ControlServer) RemoveSrc(id string) (amber.Status, error) {
return c.daemon.RemoveSource(id)
}
func (c *ControlServer) ListSrcs() ([]amber.SourceConfig, error) {
m := c.daemon.GetSources()
v := make([]amber.SourceConfig, 0, len(m))
for _, src := range m {
c := *src.GetConfig()
c.StatusConfig.Enabled = src.Enabled()
v = append(v, c)
}
return v, nil
}
func (c *ControlServer) GetUpdateComplete(name string, ver, mer *string) (zx.Channel, error) {
r, ch, e := zx.NewChannel(0)
if e != nil {
log.Printf("Could not create channel")
// TODO(raggi): the client is just going to get peer closed, and no indication of why
return zx.Channel(zx.HandleInvalid), e
}
if len(name) == 0 {
return zx.Channel(zx.HandleInvalid), fmt.Errorf("No package name provided")
}
var (
version string
merkle string
)
if ver != nil {
version = *ver
}
if mer != nil {
merkle = *mer
}
go func() {
c.daemon.UpdateIfStale()
root, length, err := c.daemon.MerkleFor(name, version, merkle)
if err != nil {
log.Printf("control_server: could not get update for %s: %s", filepath.Join(name, version, merkle), err)
ch.Handle().SignalPeer(0, zx.SignalUser0)
ch.Write([]byte(err.Error()), []zx.Handle{}, 0)
ch.Close()
return
}
if _, err := os.Stat(filepath.Join("/pkgfs/versions", root)); err == nil {
ch.Write([]byte(root), []zx.Handle{}, 0)
ch.Close()
return
}
log.Printf("control_server: get update: %s", filepath.Join(name, version, merkle))
c.daemon.AddWatch(root, func(root string, err error) {
if os.IsExist(err) {
log.Printf("control_server: %s already installed", filepath.Join(name, version, root))
// signal success to the client
err = nil
}
if err != nil {
log.Printf("control_server: error downloading package: %s", err)
ch.Handle().SignalPeer(0, zx.SignalUser0)
ch.Write([]byte(err.Error()), []zx.Handle{}, 0)
ch.Close()
return
}
ch.Write([]byte(root), []zx.Handle{}, 0)
ch.Close()
return
})
// errors are handled by the watcher callback
c.daemon.GetPkg(root, length)
}()
return r, nil
}
func (c *ControlServer) PackagesActivated(merkle []string) error {
log.Printf("control_server: packages activated %s", merkle)
for _, m := range merkle {
c.daemon.Activated(m)
}
return nil
}
func (c *ControlServer) PackagesFailed(merkle []string, status int32, blobMerkle string) error {
log.Printf("control_server: packages failed %s due to blob %s, status: %d", merkle, blobMerkle, status)
for _, m := range merkle {
c.daemon.Failed(m, zx.Status(status))
}
return nil
}
func (c *ControlServer) SetSrcEnabled(id string, enabled bool) (amber.Status, error) {
if enabled {
if err := c.daemon.EnableSource(id); err != nil {
return amber.StatusErr, nil
}
} else {
if err := c.daemon.DisableSource(id); err != nil {
return amber.StatusErr, nil
}
}
return amber.StatusOk, nil
}
func (c *ControlServer) GetBlob(merkle string) error {
log.Printf("control_server: blob requested: %q", merkle)
if !merklePat.MatchString(merkle) {
return fmt.Errorf("%q is not a valid merkle root", merkle)
}
go c.daemon.GetBlob(merkle)
return nil
}
func (c *ControlServer) Login(srcId string) (*amber.DeviceCode, error) {
return c.daemon.Login(srcId)
}
func (c *ControlServer) Gc() error {
return c.daemon.GC()
}