safing-portmaster/updates/export.go

129 lines
2.5 KiB
Go

package updates
import (
"context"
"errors"
"sync"
"github.com/safing/portbase/database"
"github.com/safing/portbase/database/query"
"github.com/safing/portbase/database/record"
"github.com/safing/portbase/info"
"github.com/safing/portbase/log"
"github.com/safing/portbase/updater"
)
// database key for update information
const (
versionsDBKey = "core:status/versions"
)
// working vars
var (
versionExport *versions
versionExportDB = database.NewInterface(nil)
versionExportHook *database.RegisteredHook
)
// versions holds updates status information.
type versions struct {
record.Base
lock sync.Mutex
Core *info.Info
Resources map[string]*updater.Resource
internalSave bool
}
func initVersionExport() (err error) {
// init export struct
versionExport = &versions{
internalSave: true,
}
versionExport.SetKey(versionsDBKey)
// attach hook to database
versionExportHook, err = database.RegisterHook(query.New(versionsDBKey), &exportHook{})
if err != nil {
return err
}
module.RegisterEventHook(
"updates",
eventVersionUpdate,
"export version status",
export,
)
return nil
}
func stopVersionExport() error {
return versionExportHook.Cancel()
}
var exportMicroTaskName = "update version status"
func export(_ context.Context, _ interface{}) error {
// populate
versionExport.lock.Lock()
versionExport.Core = info.GetInfo()
versionExport.Resources = registry.Export()
versionExport.lock.Unlock()
// save
err := versionExportDB.Put(versionExport)
if err != nil {
log.Warningf("updates: failed to export versions: %s", err)
}
return nil
}
// Lock locks the versionExport and all associated resources.
func (v *versions) Lock() {
// lock self
v.lock.Lock()
// lock all resources
for _, res := range v.Resources {
res.Lock()
}
}
// Lock unlocks the versionExport and all associated resources.
func (v *versions) Unlock() {
// unlock all resources
for _, res := range v.Resources {
res.Unlock()
}
// unlock self
v.lock.Unlock()
}
type exportHook struct {
database.HookBase
}
// UsesPrePut implements the Hook interface.
func (eh *exportHook) UsesPrePut() bool {
return true
}
var errInternalRecord = errors.New("may not modify internal record")
// PrePut implements the Hook interface.
func (eh *exportHook) PrePut(r record.Record) (record.Record, error) {
if r.IsWrapped() {
return nil, errInternalRecord
}
ve, ok := r.(*versions)
if !ok {
return nil, errInternalRecord
}
if !ve.internalSave {
return nil, errInternalRecord
}
return r, nil
}