[WIP] updater rafactoring, minor improvments
This commit is contained in:
@@ -35,20 +35,12 @@ type windowsService struct {
|
|||||||
func (ws *windowsService) Execute(args []string, changeRequests <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
|
func (ws *windowsService) Execute(args []string, changeRequests <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
|
||||||
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
|
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
|
||||||
changes <- svc.Status{State: svc.StartPending}
|
changes <- svc.Status{State: svc.StartPending}
|
||||||
|
ws.instance.Start()
|
||||||
startupComplete := make(chan struct{})
|
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
|
||||||
go func() {
|
|
||||||
for !ws.instance.Ready() {
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
|
||||||
startupComplete <- struct{}{}
|
|
||||||
}()
|
|
||||||
|
|
||||||
service:
|
service:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-startupComplete:
|
|
||||||
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
|
|
||||||
case <-ws.instance.Stopped():
|
case <-ws.instance.Stopped():
|
||||||
changes <- svc.Status{State: svc.StopPending}
|
changes <- svc.Status{State: svc.StopPending}
|
||||||
break service
|
break service
|
||||||
@@ -59,13 +51,13 @@ service:
|
|||||||
case svc.Stop, svc.Shutdown:
|
case svc.Stop, svc.Shutdown:
|
||||||
ws.instance.Shutdown()
|
ws.instance.Shutdown()
|
||||||
default:
|
default:
|
||||||
log.Errorf("unexpected control request: #%d\n", c)
|
log.Errorf("unexpected control request: #%d", c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait until everything else is finished
|
// wait until everything else is finished
|
||||||
finishWg.Wait()
|
// finishWg.Wait()
|
||||||
|
|
||||||
log.Shutdown()
|
log.Shutdown()
|
||||||
|
|
||||||
@@ -88,12 +80,12 @@ func run(instance *service.Instance) error {
|
|||||||
// select service run type
|
// select service run type
|
||||||
svcRun := svc.Run
|
svcRun := svc.Run
|
||||||
if !isService {
|
if !isService {
|
||||||
log.Warningf("running interactively, switching to debug execution (no real service).\n")
|
log.Warningf("running interactively, switching to debug execution (no real service).")
|
||||||
svcRun = debug.Run
|
svcRun = debug.Run
|
||||||
go registerSignalHandler(instance)
|
go registerSignalHandler(instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
runWg.Add(2)
|
runWg.Add(1)
|
||||||
|
|
||||||
// run service client
|
// run service client
|
||||||
go func() {
|
go func() {
|
||||||
@@ -105,29 +97,27 @@ func run(instance *service.Instance) error {
|
|||||||
} else {
|
} else {
|
||||||
log.Infof("shuting down service")
|
log.Infof("shuting down service")
|
||||||
}
|
}
|
||||||
instance.Shutdown()
|
|
||||||
runWg.Done()
|
runWg.Done()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
finishWg.Add(1)
|
// finishWg.Add(1)
|
||||||
// run service
|
// run service
|
||||||
go func() {
|
// go func() {
|
||||||
// run slightly delayed
|
// // run slightly delayed
|
||||||
time.Sleep(250 * time.Millisecond)
|
// time.Sleep(250 * time.Millisecond)
|
||||||
instance.Start()
|
|
||||||
|
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
fmt.Printf("instance start failed: %s\n", err)
|
// fmt.Printf("instance start failed: %s\n", err)
|
||||||
|
|
||||||
// Print stack on start failure, if enabled.
|
// // Print stack on start failure, if enabled.
|
||||||
if printStackOnExit {
|
// if printStackOnExit {
|
||||||
printStackTo(os.Stdout, "PRINTING STACK ON START FAILURE")
|
// printStackTo(os.Stdout, "PRINTING STACK ON START FAILURE")
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
// }
|
||||||
runWg.Done()
|
// runWg.Done()
|
||||||
finishWg.Done()
|
// finishWg.Done()
|
||||||
}()
|
// }()
|
||||||
|
|
||||||
runWg.Wait()
|
runWg.Wait()
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ Update WIX installer template:
|
|||||||
2. Replace the contents of `templates/main_original.wxs` with the repository version.
|
2. Replace the contents of `templates/main_original.wxs` with the repository version.
|
||||||
3. Replace the contents of `templates/main.wsx` and add the fallowing lines at the end of the file, inside the `Product` tag.
|
3. Replace the contents of `templates/main.wsx` and add the fallowing lines at the end of the file, inside the `Product` tag.
|
||||||
```xml
|
```xml
|
||||||
|
<!-- Service fragments -->
|
||||||
<CustomActionRef Id='InstallPortmasterService' />
|
<CustomActionRef Id='InstallPortmasterService' />
|
||||||
<CustomActionRef Id='StopPortmasterService' />
|
<CustomActionRef Id='StopPortmasterService' />
|
||||||
<CustomActionRef Id='DeletePortmasterService' />
|
<CustomActionRef Id='DeletePortmasterService' />
|
||||||
|
<!-- End Service fragments -->
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -109,8 +109,10 @@
|
|||||||
},
|
},
|
||||||
"wix": {
|
"wix": {
|
||||||
"fragmentPaths": [
|
"fragmentPaths": [
|
||||||
"templates/service.wxs"
|
"templates/service.wxs",
|
||||||
|
"templates/files.wxs"
|
||||||
],
|
],
|
||||||
|
"componentGroupRefs": ["BinaryAndIntelFiles"],
|
||||||
"template": "templates/main.wxs"
|
"template": "templates/main.wxs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
36
desktop/tauri/src-tauri/templates/files.wxs
Normal file
36
desktop/tauri/src-tauri/templates/files.wxs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||||
|
<Fragment>
|
||||||
|
<DirectoryRef Id="INSTALLDIR">
|
||||||
|
<Directory Id="BinaryDir" Name="binary" />
|
||||||
|
<Directory Id="IntelDir" Name="intel" />
|
||||||
|
</DirectoryRef>
|
||||||
|
</Fragment>
|
||||||
|
|
||||||
|
<Fragment>
|
||||||
|
<Component Id="BinaryFiles" Directory="BinaryDir" Guid="850cdd31-424d-45f5-b8f0-95df950ebd0d">
|
||||||
|
<File Id="BinIndexJson" Source="..\..\..\..\binaries\bin-index.json" />
|
||||||
|
<File Id="PortmasterCoreExe" Source="..\..\..\..\binaries\portmaster-core.exe" />
|
||||||
|
<File Id="PortmasterKextSys" Source="..\..\..\..\binaries\portmaster-kext.sys" />
|
||||||
|
<File Id="PortmasterZip" Source="..\..\..\..\binaries\portmaster.zip" />
|
||||||
|
<File Id="AssetsZip" Source="..\..\..\..\binaries\assets.zip" />
|
||||||
|
</Component>
|
||||||
|
|
||||||
|
<Component Id="IntelFiles" Directory="IntelDir" Guid="0bb439f1-2075-45b0-95bf-78ed3dffeb69">
|
||||||
|
<File Id="IntelIndexJson" Source="..\..\..\..\binaries\intel-index.json" />
|
||||||
|
<File Id="BaseDsdl" Source="..\..\..\..\binaries\base.dsdl" />
|
||||||
|
<File Id="Geoipv4Mmdb" Source="..\..\..\..\binaries\geoipv4.mmdb" />
|
||||||
|
<File Id="Geoipv6Mmdb" Source="..\..\..\..\binaries\geoipv6.mmdb" />
|
||||||
|
<File Id="IndexDsd" Source="..\..\..\..\binaries\index.dsd" />
|
||||||
|
<File Id="IntermediateDsdl" Source="..\..\..\..\binaries\intermediate.dsdl" />
|
||||||
|
<File Id="UrgentDsdl" Source="..\..\..\..\binaries\urgent.dsdl" />
|
||||||
|
</Component>
|
||||||
|
</Fragment>
|
||||||
|
|
||||||
|
<Fragment>
|
||||||
|
<ComponentGroup Id="BinaryAndIntelFiles">
|
||||||
|
<ComponentRef Id="BinaryFiles" />
|
||||||
|
<ComponentRef Id="IntelFiles" />
|
||||||
|
</ComponentGroup>
|
||||||
|
</Fragment>
|
||||||
|
</Wix>
|
||||||
@@ -341,14 +341,16 @@
|
|||||||
</InstallExecuteSequence>
|
</InstallExecuteSequence>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<CustomActionRef Id='InstallPortmasterService' />
|
|
||||||
<CustomActionRef Id='StopPortmasterService' />
|
|
||||||
<CustomActionRef Id='DeletePortmasterService' />
|
|
||||||
|
|
||||||
<InstallExecuteSequence>
|
<InstallExecuteSequence>
|
||||||
<Custom Action="LaunchApplication" After="InstallFinalize">AUTOLAUNCHAPP AND NOT Installed</Custom>
|
<Custom Action="LaunchApplication" After="InstallFinalize">AUTOLAUNCHAPP AND NOT Installed</Custom>
|
||||||
</InstallExecuteSequence>
|
</InstallExecuteSequence>
|
||||||
|
|
||||||
<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLDIR]" After="CostFinalize"/>
|
<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLDIR]" After="CostFinalize"/>
|
||||||
|
|
||||||
|
<!-- Service fragments -->
|
||||||
|
<CustomActionRef Id='InstallPortmasterService' />
|
||||||
|
<CustomActionRef Id='StopPortmasterService' />
|
||||||
|
<CustomActionRef Id='DeletePortmasterService' />
|
||||||
|
<!-- End Service fragments -->
|
||||||
</Product>
|
</Product>
|
||||||
</Wix>
|
</Wix>
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<Fragment>
|
<Fragment>
|
||||||
<CustomAction Id="InstallPortmasterService"
|
<CustomAction Id="InstallPortmasterService"
|
||||||
Directory="INSTALLDIR"
|
Directory="INSTALLDIR"
|
||||||
ExeCommand=""[INSTALLDIR]portmaster-start.exe" install core-service --data="[INSTALLDIR]data""
|
ExeCommand="sc.exe create PortmasterCore binPath= "[INSTALLDIR]binary\portmaster-core.exe --data [INSTALLDIR]data""
|
||||||
Execute="commit"
|
Execute="commit"
|
||||||
Return="check"
|
Return="check"
|
||||||
Impersonate="no"
|
Impersonate="no"
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
go_runtime "runtime"
|
go_runtime "runtime"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@@ -35,7 +37,6 @@ import (
|
|||||||
"github.com/safing/portmaster/service/sync"
|
"github.com/safing/portmaster/service/sync"
|
||||||
"github.com/safing/portmaster/service/ui"
|
"github.com/safing/portmaster/service/ui"
|
||||||
"github.com/safing/portmaster/service/updates"
|
"github.com/safing/portmaster/service/updates"
|
||||||
"github.com/safing/portmaster/service/updates/registry"
|
|
||||||
"github.com/safing/portmaster/spn/access"
|
"github.com/safing/portmaster/spn/access"
|
||||||
"github.com/safing/portmaster/spn/cabin"
|
"github.com/safing/portmaster/spn/cabin"
|
||||||
"github.com/safing/portmaster/spn/captain"
|
"github.com/safing/portmaster/spn/captain"
|
||||||
@@ -103,31 +104,54 @@ type Instance struct {
|
|||||||
CommandLineOperation func() error
|
CommandLineOperation func() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getCurrentBinaryFolder() (string, error) {
|
||||||
|
// Get the path of the currently running executable
|
||||||
|
exePath, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to get executable path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the absolute path
|
||||||
|
absPath, err := filepath.Abs(exePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to get absolute path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the directory of the executable
|
||||||
|
installDir := filepath.Dir(absPath)
|
||||||
|
|
||||||
|
return installDir, nil
|
||||||
|
}
|
||||||
|
|
||||||
// New returns a new Portmaster service instance.
|
// New returns a new Portmaster service instance.
|
||||||
func New(svcCfg *ServiceConfig) (*Instance, error) { //nolint:maintidx
|
func New(svcCfg *ServiceConfig) (*Instance, error) { //nolint:maintidx
|
||||||
var binaryUpdateIndex registry.UpdateIndex
|
var binaryUpdateIndex updates.UpdateIndex
|
||||||
var intelUpdateIndex registry.UpdateIndex
|
var intelUpdateIndex updates.UpdateIndex
|
||||||
if go_runtime.GOOS == "windows" {
|
if go_runtime.GOOS == "windows" {
|
||||||
binaryUpdateIndex = registry.UpdateIndex{
|
binaryFolder, err := getCurrentBinaryFolder()
|
||||||
Directory: "C:/Program Files/Portmaster/binary",
|
if err != nil {
|
||||||
DownloadDirectory: "C:/Program Files/Portmaster/new_binary",
|
return nil, err
|
||||||
PurgeDirectory: "C:/Program Files/Portmaster/old_binary",
|
}
|
||||||
|
binaryUpdateIndex = updates.UpdateIndex{
|
||||||
|
Directory: binaryFolder, // Default: C:/Program Files/Portmaster/binary
|
||||||
|
DownloadDirectory: os.ExpandEnv("%ProgramData%/Portmaster/new_binary"),
|
||||||
|
PurgeDirectory: os.ExpandEnv("%ProgramData%/Portmaster/old_binary"),
|
||||||
Ignore: []string{"databases", "intel", "config.json"},
|
Ignore: []string{"databases", "intel", "config.json"},
|
||||||
IndexURLs: []string{"http://192.168.88.11:8000/test-binary.json"},
|
IndexURLs: []string{"http://192.168.88.11:8000/test-binary.json"},
|
||||||
IndexFile: "bin-index.json",
|
IndexFile: "bin-index.json",
|
||||||
AutoApply: false,
|
AutoApply: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
intelUpdateIndex = registry.UpdateIndex{
|
intelUpdateIndex = updates.UpdateIndex{
|
||||||
Directory: "C:/Program Files/Portmaster/intel",
|
Directory: os.ExpandEnv("%ProgramData%/Portmaster/intel"),
|
||||||
DownloadDirectory: "C:/Program Files/Portmaster/new_intel",
|
DownloadDirectory: os.ExpandEnv("%ProgramData%/Portmaster/new_intel"),
|
||||||
PurgeDirectory: "C:/Program Files/Portmaster/old_intel",
|
PurgeDirectory: os.ExpandEnv("%ProgramData%/Portmaster/old_intel"),
|
||||||
IndexURLs: []string{"http://192.168.88.11:8000/test-intel.json"},
|
IndexURLs: []string{"http://192.168.88.11:8000/test-intel.json"},
|
||||||
IndexFile: "intel-index.json",
|
IndexFile: "intel-index.json",
|
||||||
AutoApply: true,
|
AutoApply: true,
|
||||||
}
|
}
|
||||||
} else if go_runtime.GOOS == "linux" {
|
} else if go_runtime.GOOS == "linux" {
|
||||||
binaryUpdateIndex = registry.UpdateIndex{
|
binaryUpdateIndex = updates.UpdateIndex{
|
||||||
Directory: "/usr/lib/portmaster",
|
Directory: "/usr/lib/portmaster",
|
||||||
DownloadDirectory: "/var/lib/portmaster/new_bin",
|
DownloadDirectory: "/var/lib/portmaster/new_bin",
|
||||||
PurgeDirectory: "/var/lib/portmaster/old_bin",
|
PurgeDirectory: "/var/lib/portmaster/old_bin",
|
||||||
@@ -137,7 +161,7 @@ func New(svcCfg *ServiceConfig) (*Instance, error) { //nolint:maintidx
|
|||||||
AutoApply: false,
|
AutoApply: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
intelUpdateIndex = registry.UpdateIndex{
|
intelUpdateIndex = updates.UpdateIndex{
|
||||||
Directory: "/var/lib/portmaster/intel",
|
Directory: "/var/lib/portmaster/intel",
|
||||||
DownloadDirectory: "/var/lib/portmaster/new_intel",
|
DownloadDirectory: "/var/lib/portmaster/new_intel",
|
||||||
PurgeDirectory: "/var/lib/portmaster/intel_bin",
|
PurgeDirectory: "/var/lib/portmaster/intel_bin",
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
"github.com/safing/portmaster/base/database"
|
"github.com/safing/portmaster/base/database"
|
||||||
"github.com/safing/portmaster/base/database/record"
|
"github.com/safing/portmaster/base/database/record"
|
||||||
"github.com/safing/portmaster/base/log"
|
"github.com/safing/portmaster/base/log"
|
||||||
"github.com/safing/portmaster/service/updates/registry"
|
"github.com/safing/portmaster/service/updates"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -39,9 +39,9 @@ var (
|
|||||||
filterListLock sync.RWMutex
|
filterListLock sync.RWMutex
|
||||||
|
|
||||||
// Updater files for tracking upgrades.
|
// Updater files for tracking upgrades.
|
||||||
baseFile *registry.File
|
baseFile *updates.File
|
||||||
intermediateFile *registry.File
|
intermediateFile *updates.File
|
||||||
urgentFile *registry.File
|
urgentFile *updates.File
|
||||||
|
|
||||||
filterListsLoaded chan struct{}
|
filterListsLoaded chan struct{}
|
||||||
)
|
)
|
||||||
@@ -77,7 +77,7 @@ func isLoaded() bool {
|
|||||||
|
|
||||||
// processListFile opens the latest version of file and decodes it's DSDL
|
// processListFile opens the latest version of file and decodes it's DSDL
|
||||||
// content. It calls processEntry for each decoded filterlists entry.
|
// content. It calls processEntry for each decoded filterlists entry.
|
||||||
func processListFile(ctx context.Context, filter *scopedBloom, file *registry.File) error {
|
func processListFile(ctx context.Context, filter *scopedBloom, file *updates.File) error {
|
||||||
f, err := os.Open(file.Path())
|
f, err := os.Open(file.Path())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/safing/portmaster/base/database"
|
"github.com/safing/portmaster/base/database"
|
||||||
"github.com/safing/portmaster/base/database/record"
|
"github.com/safing/portmaster/base/database/record"
|
||||||
"github.com/safing/portmaster/base/log"
|
"github.com/safing/portmaster/base/log"
|
||||||
"github.com/safing/portmaster/service/updates/registry"
|
"github.com/safing/portmaster/service/updates"
|
||||||
"github.com/safing/structures/dsd"
|
"github.com/safing/structures/dsd"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ func getListIndexFromCache() (*ListIndexFile, error) {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// listIndexUpdate must only be used by updateListIndex.
|
// listIndexUpdate must only be used by updateListIndex.
|
||||||
listIndexUpdate *registry.File
|
listIndexUpdate *updates.File
|
||||||
listIndexUpdateLock sync.Mutex
|
listIndexUpdateLock sync.Mutex
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
"github.com/safing/portmaster/base/database/query"
|
"github.com/safing/portmaster/base/database/query"
|
||||||
"github.com/safing/portmaster/base/log"
|
"github.com/safing/portmaster/base/log"
|
||||||
"github.com/safing/portmaster/service/mgr"
|
"github.com/safing/portmaster/service/mgr"
|
||||||
"github.com/safing/portmaster/service/updates/registry"
|
"github.com/safing/portmaster/service/updates"
|
||||||
)
|
)
|
||||||
|
|
||||||
var updateInProgress = abool.New()
|
var updateInProgress = abool.New()
|
||||||
@@ -174,8 +174,8 @@ func removeAllObsoleteFilterEntries(wc *mgr.WorkerCtx) error {
|
|||||||
// getUpgradableFiles returns a slice of filterlists files
|
// getUpgradableFiles returns a slice of filterlists files
|
||||||
// that should be updated. The files MUST be updated and
|
// that should be updated. The files MUST be updated and
|
||||||
// processed in the returned order!
|
// processed in the returned order!
|
||||||
func getUpgradableFiles() ([]*registry.File, error) {
|
func getUpgradableFiles() ([]*updates.File, error) {
|
||||||
var updateOrder []*registry.File
|
var updateOrder []*updates.File
|
||||||
|
|
||||||
// cacheDBInUse := isLoaded()
|
// cacheDBInUse := isLoaded()
|
||||||
|
|
||||||
@@ -218,7 +218,7 @@ func getUpgradableFiles() ([]*registry.File, error) {
|
|||||||
return resolveUpdateOrder(updateOrder)
|
return resolveUpdateOrder(updateOrder)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveUpdateOrder(updateOrder []*registry.File) ([]*registry.File, error) {
|
func resolveUpdateOrder(updateOrder []*updates.File) ([]*updates.File, error) {
|
||||||
// sort the update order by ascending version
|
// sort the update order by ascending version
|
||||||
sort.Sort(byAscVersion(updateOrder))
|
sort.Sort(byAscVersion(updateOrder))
|
||||||
log.Tracef("intel/filterlists: order of updates: %v", updateOrder)
|
log.Tracef("intel/filterlists: order of updates: %v", updateOrder)
|
||||||
@@ -258,7 +258,7 @@ func resolveUpdateOrder(updateOrder []*registry.File) ([]*registry.File, error)
|
|||||||
return updateOrder[startAtIdx:], nil
|
return updateOrder[startAtIdx:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type byAscVersion []*registry.File
|
type byAscVersion []*updates.File
|
||||||
|
|
||||||
func (fs byAscVersion) Len() int { return len(fs) }
|
func (fs byAscVersion) Len() int { return len(fs) }
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/safing/portmaster/base/log"
|
"github.com/safing/portmaster/base/log"
|
||||||
"github.com/safing/portmaster/service/mgr"
|
"github.com/safing/portmaster/service/mgr"
|
||||||
"github.com/safing/portmaster/service/updates/registry"
|
"github.com/safing/portmaster/service/updates"
|
||||||
)
|
)
|
||||||
|
|
||||||
var worker *updateWorker
|
var worker *updateWorker
|
||||||
@@ -27,7 +27,7 @@ const (
|
|||||||
|
|
||||||
type geoIPDB struct {
|
type geoIPDB struct {
|
||||||
*maxminddb.Reader
|
*maxminddb.Reader
|
||||||
file *registry.File
|
file *updates.File
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateBroadcaster stores a geoIPDB and provides synchronized
|
// updateBroadcaster stores a geoIPDB and provides synchronized
|
||||||
@@ -197,7 +197,7 @@ func getGeoIPDB(resource string) (*geoIPDB, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func open(resource string) (*registry.File, error) {
|
func open(resource string) (*updates.File, error) {
|
||||||
f, err := module.instance.IntelUpdates().GetFile(resource)
|
f, err := module.instance.IntelUpdates().GetFile(resource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("getting file: %w", err)
|
return nil, fmt.Errorf("getting file: %w", err)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
"github.com/safing/portmaster/base/api"
|
"github.com/safing/portmaster/base/api"
|
||||||
"github.com/safing/portmaster/base/log"
|
"github.com/safing/portmaster/base/log"
|
||||||
"github.com/safing/portmaster/base/utils"
|
"github.com/safing/portmaster/base/utils"
|
||||||
"github.com/safing/portmaster/service/updates/registry"
|
"github.com/safing/portmaster/service/updates"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -93,7 +93,7 @@ func (bs *archiveServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
// get file from update system
|
// get file from update system
|
||||||
zipFile, err := module.instance.BinaryUpdates().GetFile(fmt.Sprintf("%s.zip", moduleName))
|
zipFile, err := module.instance.BinaryUpdates().GetFile(fmt.Sprintf("%s.zip", moduleName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, registry.ErrNotFound) {
|
if errors.Is(err, updates.ErrNotFound) {
|
||||||
log.Tracef("ui: requested module %s does not exist", moduleName)
|
log.Tracef("ui: requested module %s does not exist", moduleName)
|
||||||
http.Error(w, err.Error(), http.StatusNotFound)
|
http.Error(w, err.Error(), http.StatusNotFound)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package registry
|
package updates
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -20,7 +21,7 @@ import (
|
|||||||
|
|
||||||
const MaxUnpackSize = 1 << 30 // 2^30 == 1GB
|
const MaxUnpackSize = 1 << 30 // 2^30 == 1GB
|
||||||
|
|
||||||
const current_platform = runtime.GOOS + "_" + runtime.GOARCH
|
const currentPlatform = runtime.GOOS + "_" + runtime.GOARCH
|
||||||
|
|
||||||
type Artifact struct {
|
type Artifact struct {
|
||||||
Filename string `json:"Filename"`
|
Filename string `json:"Filename"`
|
||||||
@@ -38,7 +39,73 @@ type Bundle struct {
|
|||||||
Artifacts []Artifact `json:"Artifacts"`
|
Artifacts []Artifact `json:"Artifacts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bundle Bundle) downloadAndVerify(dir string) {
|
func ParseBundle(dir string, indexFile string) (*Bundle, error) {
|
||||||
|
filepath := fmt.Sprintf("%s/%s", dir, indexFile)
|
||||||
|
// Check if the file exists.
|
||||||
|
file, err := os.Open(filepath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open index file: %w", err)
|
||||||
|
}
|
||||||
|
defer func() { _ = file.Close() }()
|
||||||
|
|
||||||
|
// Read
|
||||||
|
content, err := io.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse
|
||||||
|
var bundle Bundle
|
||||||
|
err = json.Unmarshal(content, &bundle)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &bundle, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyMatchingFilesFromCurrent check if there the current bundle files has matching files with the new bundle and copies them if they match.
|
||||||
|
func (bundle Bundle) CopyMatchingFilesFromCurrent(current Bundle, currentDir, newDir string) error {
|
||||||
|
for _, currentArtifact := range current.Artifacts {
|
||||||
|
new:
|
||||||
|
for _, newArtifact := range bundle.Artifacts {
|
||||||
|
if currentArtifact.Filename == newArtifact.Filename {
|
||||||
|
if currentArtifact.SHA256 == newArtifact.SHA256 {
|
||||||
|
// Files match, make sure new dir exists
|
||||||
|
_ = os.MkdirAll(newDir, defaultDirMode)
|
||||||
|
|
||||||
|
// Open the current file
|
||||||
|
sourceFilePath := fmt.Sprintf("%s/%s", currentDir, newArtifact.Filename)
|
||||||
|
sourceFile, err := os.Open(sourceFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open %s file: %w", sourceFilePath, err)
|
||||||
|
}
|
||||||
|
defer sourceFile.Close()
|
||||||
|
|
||||||
|
// Create new file
|
||||||
|
destFilePath := fmt.Sprintf("%s/%s", newDir, newArtifact.Filename)
|
||||||
|
destFile, err := os.Create(destFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create %s file: %w", destFilePath, err)
|
||||||
|
}
|
||||||
|
defer destFile.Close()
|
||||||
|
|
||||||
|
// Copy
|
||||||
|
_, err = io.Copy(destFile, sourceFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to copy contents: %w", err)
|
||||||
|
}
|
||||||
|
// Flush
|
||||||
|
_ = destFile.Sync()
|
||||||
|
|
||||||
|
}
|
||||||
|
break new
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bundle Bundle) DownloadAndVerify(dir string) {
|
||||||
client := http.Client{}
|
client := http.Client{}
|
||||||
for _, artifact := range bundle.Artifacts {
|
for _, artifact := range bundle.Artifacts {
|
||||||
|
|
||||||
@@ -111,7 +178,7 @@ func checkIfFileIsValid(filename string, artifact Artifact) (bool, error) {
|
|||||||
|
|
||||||
func processArtifact(client *http.Client, artifact Artifact, filePath string) error {
|
func processArtifact(client *http.Client, artifact Artifact, filePath string) error {
|
||||||
// Skip artifacts not meant for this machine.
|
// Skip artifacts not meant for this machine.
|
||||||
if artifact.Platform != "" && artifact.Platform != current_platform {
|
if artifact.Platform != "" && artifact.Platform != currentPlatform {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package registry
|
package updates
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -19,7 +19,7 @@ type UpdateIndex struct {
|
|||||||
AutoApply bool
|
AutoApply bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *UpdateIndex) downloadIndexFile() (err error) {
|
func (ui *UpdateIndex) DownloadIndexFile() (err error) {
|
||||||
_ = os.MkdirAll(ui.DownloadDirectory, defaultDirMode)
|
_ = os.MkdirAll(ui.DownloadDirectory, defaultDirMode)
|
||||||
for _, url := range ui.IndexURLs {
|
for _, url := range ui.IndexURLs {
|
||||||
err = ui.downloadIndexFileFromURL(url)
|
err = ui.downloadIndexFileFromURL(url)
|
||||||
@@ -1,23 +1,38 @@
|
|||||||
package updates
|
package updates
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
semver "github.com/hashicorp/go-version"
|
||||||
"github.com/safing/portmaster/base/api"
|
"github.com/safing/portmaster/base/api"
|
||||||
"github.com/safing/portmaster/base/config"
|
"github.com/safing/portmaster/base/config"
|
||||||
"github.com/safing/portmaster/base/log"
|
"github.com/safing/portmaster/base/log"
|
||||||
"github.com/safing/portmaster/base/notifications"
|
"github.com/safing/portmaster/base/notifications"
|
||||||
"github.com/safing/portmaster/service/mgr"
|
"github.com/safing/portmaster/service/mgr"
|
||||||
"github.com/safing/portmaster/service/updates/registry"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var autoUpdate bool
|
type File struct {
|
||||||
|
id string
|
||||||
func init() {
|
path string
|
||||||
flag.BoolVar(&autoUpdate, "auto-update", false, "auto apply downloaded updates")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *File) Identifier() string {
|
||||||
|
return f.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) Path() string {
|
||||||
|
return f.path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) Version() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrNotFound error = errors.New("file not found")
|
||||||
|
|
||||||
// Updates provides access to released artifacts.
|
// Updates provides access to released artifacts.
|
||||||
type Updates struct {
|
type Updates struct {
|
||||||
m *mgr.Manager
|
m *mgr.Manager
|
||||||
@@ -29,13 +44,18 @@ type Updates struct {
|
|||||||
EventResourcesUpdated *mgr.EventMgr[struct{}]
|
EventResourcesUpdated *mgr.EventMgr[struct{}]
|
||||||
EventVersionsUpdated *mgr.EventMgr[struct{}]
|
EventVersionsUpdated *mgr.EventMgr[struct{}]
|
||||||
|
|
||||||
registry registry.Registry
|
updateIndex UpdateIndex
|
||||||
|
|
||||||
|
bundle *Bundle
|
||||||
|
updateBundle *Bundle
|
||||||
|
|
||||||
|
files map[string]File
|
||||||
|
|
||||||
instance instance
|
instance instance
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new Updates module.
|
// New returns a new Updates module.
|
||||||
func New(instance instance, name string, index registry.UpdateIndex) (*Updates, error) {
|
func New(instance instance, name string, index UpdateIndex) (*Updates, error) {
|
||||||
m := mgr.New(name)
|
m := mgr.New(name)
|
||||||
module := &Updates{
|
module := &Updates{
|
||||||
m: m,
|
m: m,
|
||||||
@@ -44,6 +64,8 @@ func New(instance instance, name string, index registry.UpdateIndex) (*Updates,
|
|||||||
EventResourcesUpdated: mgr.NewEventMgr[struct{}](ResourceUpdateEvent, m),
|
EventResourcesUpdated: mgr.NewEventMgr[struct{}](ResourceUpdateEvent, m),
|
||||||
EventVersionsUpdated: mgr.NewEventMgr[struct{}](VersionUpdateEvent, m),
|
EventVersionsUpdated: mgr.NewEventMgr[struct{}](VersionUpdateEvent, m),
|
||||||
|
|
||||||
|
updateIndex: index,
|
||||||
|
|
||||||
instance: instance,
|
instance: instance,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,39 +73,87 @@ func New(instance instance, name string, index registry.UpdateIndex) (*Updates,
|
|||||||
module.updateCheckWorkerMgr = m.NewWorkerMgr("update checker", module.checkForUpdates, nil)
|
module.updateCheckWorkerMgr = m.NewWorkerMgr("update checker", module.checkForUpdates, nil)
|
||||||
module.updateCheckWorkerMgr.Repeat(30 * time.Second)
|
module.updateCheckWorkerMgr.Repeat(30 * time.Second)
|
||||||
module.upgraderWorkerMgr = m.NewWorkerMgr("upgrader", func(w *mgr.WorkerCtx) error {
|
module.upgraderWorkerMgr = m.NewWorkerMgr("upgrader", func(w *mgr.WorkerCtx) error {
|
||||||
err := module.registry.ApplyUpdates()
|
err := applyUpdates(module.updateIndex, *module.updateBundle)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO(vladimir): Send notification to UI
|
// TODO(vladimir): Send notification to UI
|
||||||
log.Errorf("updates: failed to apply updates: %s", err)
|
log.Errorf("updates: failed to apply updates: %s", err)
|
||||||
} else {
|
} else {
|
||||||
|
// TODO(vladimir): Prompt user to restart?
|
||||||
module.instance.Restart()
|
module.instance.Restart()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
module.registry = registry.New(index)
|
var err error
|
||||||
_ = module.registry.Initialize()
|
module.bundle, err = ParseBundle(module.updateIndex.Directory, module.updateIndex.IndexFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse binary bundle: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add bundle artifacts to registry.
|
||||||
|
module.processBundle(module.bundle)
|
||||||
|
|
||||||
|
// Remove old files
|
||||||
|
m.Go("old files cleaner", func(ctx *mgr.WorkerCtx) error {
|
||||||
|
err := os.RemoveAll(module.updateIndex.PurgeDirectory)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to delete folder: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
return module, nil
|
return module, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (reg *Updates) processBundle(bundle *Bundle) {
|
||||||
|
for _, artifact := range bundle.Artifacts {
|
||||||
|
artifactPath := fmt.Sprintf("%s/%s", reg.updateIndex.Directory, artifact.Filename)
|
||||||
|
reg.files[artifact.Filename] = File{id: artifact.Filename, path: artifactPath}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (u *Updates) checkForUpdates(_ *mgr.WorkerCtx) error {
|
func (u *Updates) checkForUpdates(_ *mgr.WorkerCtx) error {
|
||||||
hasUpdates, err := u.registry.CheckForUpdates()
|
err := u.updateIndex.DownloadIndexFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("updates: failed to check for updates: %s", err)
|
return fmt.Errorf("failed to download index file: %s", err)
|
||||||
}
|
}
|
||||||
if hasUpdates {
|
|
||||||
log.Infof("updates: there is updates available")
|
u.updateBundle, err = ParseBundle(u.updateIndex.DownloadDirectory, u.updateIndex.IndexFile)
|
||||||
err = u.registry.DownloadUpdates()
|
if err != nil {
|
||||||
if err != nil {
|
return fmt.Errorf("failed parse bundle: %s", err)
|
||||||
log.Errorf("updates: failed to download bundle: %s", err)
|
|
||||||
} else if autoUpdate {
|
|
||||||
u.ApplyUpdates()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Infof("updates: no new updates")
|
|
||||||
u.EventResourcesUpdated.Submit(struct{}{})
|
|
||||||
}
|
}
|
||||||
|
defer u.EventResourcesUpdated.Submit(struct{}{})
|
||||||
|
|
||||||
|
// Compare current and downloaded index version.
|
||||||
|
currentVersion, err := semver.NewVersion(u.bundle.Version)
|
||||||
|
downloadVersion, err := semver.NewVersion(u.updateBundle.Version)
|
||||||
|
if currentVersion.Compare(downloadVersion) <= 0 {
|
||||||
|
// no updates
|
||||||
|
log.Info("updates: check complete: no new updates")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("updates: check complete: downloading new version: %s %s", u.updateBundle.Name, u.updateBundle.Version)
|
||||||
|
err = u.DownloadUpdates()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("updates: failed to download bundle: %s", err)
|
||||||
|
} else if u.updateIndex.AutoApply {
|
||||||
|
u.ApplyUpdates()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DownloadUpdates downloads available binary updates.
|
||||||
|
func (u *Updates) DownloadUpdates() error {
|
||||||
|
if u.updateBundle == nil {
|
||||||
|
// CheckForBinaryUpdates needs to be called before this.
|
||||||
|
return fmt.Errorf("no valid update bundle found")
|
||||||
|
}
|
||||||
|
_ = deleteUnfinishedDownloads(u.updateIndex.DownloadDirectory)
|
||||||
|
err := u.updateBundle.CopyMatchingFilesFromCurrent(*u.bundle, u.updateIndex.Directory, u.updateIndex.DownloadDirectory)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("updates: error while coping file from current to update: %s", err)
|
||||||
|
}
|
||||||
|
u.updateBundle.DownloadAndVerify(u.updateIndex.DownloadDirectory)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,8 +177,17 @@ func (u *Updates) Start() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Updates) GetFile(id string) (*registry.File, error) {
|
func (u *Updates) GetFile(id string) (*File, error) {
|
||||||
return u.registry.GetFile(id)
|
file, ok := u.files[id]
|
||||||
|
if ok {
|
||||||
|
return &file, nil
|
||||||
|
} else {
|
||||||
|
log.Errorf("updates: requested file id not found: %s", id)
|
||||||
|
for _, file := range u.files {
|
||||||
|
log.Debugf("File: %s", file)
|
||||||
|
}
|
||||||
|
return nil, ErrNotFound
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop stops the module.
|
// Stop stops the module.
|
||||||
|
|||||||
@@ -1,210 +0,0 @@
|
|||||||
package registry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/safing/portmaster/base/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ErrNotFound error = errors.New("file not found")
|
|
||||||
|
|
||||||
const (
|
|
||||||
defaultFileMode = os.FileMode(0o0644)
|
|
||||||
executableFileMode = os.FileMode(0o0744)
|
|
||||||
defaultDirMode = os.FileMode(0o0755)
|
|
||||||
)
|
|
||||||
|
|
||||||
type File struct {
|
|
||||||
id string
|
|
||||||
path string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *File) Identifier() string {
|
|
||||||
return f.id
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *File) Path() string {
|
|
||||||
return f.path
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *File) Version() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type Registry struct {
|
|
||||||
updateIndex UpdateIndex
|
|
||||||
|
|
||||||
bundle *Bundle
|
|
||||||
updateBundle *Bundle
|
|
||||||
|
|
||||||
files map[string]File
|
|
||||||
}
|
|
||||||
|
|
||||||
// New create new Registry.
|
|
||||||
func New(index UpdateIndex) Registry {
|
|
||||||
return Registry{
|
|
||||||
updateIndex: index,
|
|
||||||
files: make(map[string]File),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize parses and initializes currently installed bundles.
|
|
||||||
func (reg *Registry) Initialize() error {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// Parse current installed binary bundle.
|
|
||||||
reg.bundle, err = parseBundle(reg.updateIndex.Directory, reg.updateIndex.IndexFile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to parse binary bundle: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add bundle artifacts to registry.
|
|
||||||
reg.processBundle(reg.bundle)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (reg *Registry) processBundle(bundle *Bundle) {
|
|
||||||
for _, artifact := range bundle.Artifacts {
|
|
||||||
artifactPath := fmt.Sprintf("%s/%s", reg.updateIndex.Directory, artifact.Filename)
|
|
||||||
reg.files[artifact.Filename] = File{id: artifact.Filename, path: artifactPath}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFile returns the object of a artifact by id.
|
|
||||||
func (reg *Registry) GetFile(id string) (*File, error) {
|
|
||||||
file, ok := reg.files[id]
|
|
||||||
if ok {
|
|
||||||
return &file, nil
|
|
||||||
} else {
|
|
||||||
log.Errorf("updates: requested file id not found: %s", id)
|
|
||||||
for _, file := range reg.files {
|
|
||||||
log.Debugf("File: %s", file)
|
|
||||||
}
|
|
||||||
return nil, ErrNotFound
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckForUpdates checks if there is a new binary bundle updates.
|
|
||||||
func (reg *Registry) CheckForUpdates() (bool, error) {
|
|
||||||
err := reg.updateIndex.downloadIndexFile()
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
reg.updateBundle, err = parseBundle(reg.updateIndex.DownloadDirectory, reg.updateIndex.IndexFile)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(vladimir): Make a better check.
|
|
||||||
if reg.bundle.Version != reg.updateBundle.Version {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DownloadUpdates downloads available binary updates.
|
|
||||||
func (reg *Registry) DownloadUpdates() error {
|
|
||||||
if reg.updateBundle == nil {
|
|
||||||
// CheckForBinaryUpdates needs to be called before this.
|
|
||||||
return fmt.Errorf("no valid update bundle found")
|
|
||||||
}
|
|
||||||
_ = deleteUnfinishedDownloads(reg.updateIndex.DownloadDirectory)
|
|
||||||
reg.updateBundle.downloadAndVerify(reg.updateIndex.DownloadDirectory)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyUpdates removes the current binary folder and replaces it with the downloaded one.
|
|
||||||
func (reg *Registry) ApplyUpdates() error {
|
|
||||||
// Create purge dir.
|
|
||||||
err := os.MkdirAll(reg.updateIndex.PurgeDirectory, defaultDirMode)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create directory: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read all files in the current version folder.
|
|
||||||
files, err := os.ReadDir(reg.updateIndex.Directory)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move current version files into purge folder.
|
|
||||||
for _, file := range files {
|
|
||||||
filepath := fmt.Sprintf("%s/%s", reg.updateIndex.Directory, file.Name())
|
|
||||||
purgePath := fmt.Sprintf("%s/%s", reg.updateIndex.PurgeDirectory, file.Name())
|
|
||||||
err := os.Rename(filepath, purgePath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to move file %s: %w", filepath, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move the new index file
|
|
||||||
indexFile := fmt.Sprintf("%s/%s", reg.updateIndex.DownloadDirectory, reg.updateIndex.IndexFile)
|
|
||||||
newIndexFile := fmt.Sprintf("%s/%s", reg.updateIndex.Directory, reg.updateIndex.IndexFile)
|
|
||||||
err = os.Rename(indexFile, newIndexFile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to move index file %s: %w", indexFile, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move downloaded files to the current version folder.
|
|
||||||
for _, artifact := range reg.bundle.Artifacts {
|
|
||||||
fromFilepath := fmt.Sprintf("%s/%s", reg.updateIndex.DownloadDirectory, artifact.Filename)
|
|
||||||
toFilepath := fmt.Sprintf("%s/%s", reg.updateIndex.Directory, artifact.Filename)
|
|
||||||
err = os.Rename(fromFilepath, toFilepath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to move file %s: %w", fromFilepath, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseBundle(dir string, indexFile string) (*Bundle, error) {
|
|
||||||
filepath := fmt.Sprintf("%s/%s", dir, indexFile)
|
|
||||||
// Check if the file exists.
|
|
||||||
file, err := os.Open(filepath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to open index file: %w", err)
|
|
||||||
}
|
|
||||||
defer func() { _ = file.Close() }()
|
|
||||||
|
|
||||||
// Read
|
|
||||||
content, err := io.ReadAll(file)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse
|
|
||||||
var bundle Bundle
|
|
||||||
err = json.Unmarshal(content, &bundle)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &bundle, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteUnfinishedDownloads(rootDir string) error {
|
|
||||||
return filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the current file has the specified extension
|
|
||||||
if !info.IsDir() && strings.HasSuffix(info.Name(), ".download") {
|
|
||||||
log.Warningf("updates: deleting unfinished: %s\n", path)
|
|
||||||
err := os.Remove(path)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to delete file %s: %w", path, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
78
service/updates/updater.go
Normal file
78
service/updates/updater.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package updates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/safing/portmaster/base/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultFileMode = os.FileMode(0o0644)
|
||||||
|
executableFileMode = os.FileMode(0o0744)
|
||||||
|
defaultDirMode = os.FileMode(0o0755)
|
||||||
|
)
|
||||||
|
|
||||||
|
func applyUpdates(updateIndex UpdateIndex, newBundle Bundle) error {
|
||||||
|
// Create purge dir.
|
||||||
|
err := os.MkdirAll(updateIndex.PurgeDirectory, defaultDirMode)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read all files in the current version folder.
|
||||||
|
files, err := os.ReadDir(updateIndex.Directory)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move current version files into purge folder.
|
||||||
|
for _, file := range files {
|
||||||
|
filepath := fmt.Sprintf("%s/%s", updateIndex.Directory, file.Name())
|
||||||
|
purgePath := fmt.Sprintf("%s/%s", updateIndex.PurgeDirectory, file.Name())
|
||||||
|
err := os.Rename(filepath, purgePath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to move file %s: %w", filepath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the new index file
|
||||||
|
indexFile := fmt.Sprintf("%s/%s", updateIndex.DownloadDirectory, updateIndex.IndexFile)
|
||||||
|
newIndexFile := fmt.Sprintf("%s/%s", updateIndex.Directory, updateIndex.IndexFile)
|
||||||
|
err = os.Rename(indexFile, newIndexFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to move index file %s: %w", indexFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move downloaded files to the current version folder.
|
||||||
|
for _, artifact := range newBundle.Artifacts {
|
||||||
|
fromFilepath := fmt.Sprintf("%s/%s", updateIndex.DownloadDirectory, artifact.Filename)
|
||||||
|
toFilepath := fmt.Sprintf("%s/%s", updateIndex.Directory, artifact.Filename)
|
||||||
|
err = os.Rename(fromFilepath, toFilepath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to move file %s: %w", fromFilepath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteUnfinishedDownloads(rootDir string) error {
|
||||||
|
return filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the current file has the specified extension
|
||||||
|
if !info.IsDir() && strings.HasSuffix(info.Name(), ".download") {
|
||||||
|
log.Warningf("updates: deleting unfinished: %s\n", path)
|
||||||
|
err := os.Remove(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to delete file %s: %w", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/safing/portmaster/service/mgr"
|
"github.com/safing/portmaster/service/mgr"
|
||||||
"github.com/safing/portmaster/service/updates/registry"
|
"github.com/safing/portmaster/service/updates"
|
||||||
"github.com/safing/portmaster/spn/conf"
|
"github.com/safing/portmaster/spn/conf"
|
||||||
"github.com/safing/portmaster/spn/hub"
|
"github.com/safing/portmaster/spn/hub"
|
||||||
"github.com/safing/portmaster/spn/navigator"
|
"github.com/safing/portmaster/spn/navigator"
|
||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
intelResource *registry.File
|
intelResource *updates.File
|
||||||
intelResourcePath = "intel/spn/main-intel.yaml"
|
intelResourcePath = "intel/spn/main-intel.yaml"
|
||||||
intelResourceMapName = "main"
|
intelResourceMapName = "main"
|
||||||
intelResourceUpdateLock sync.Mutex
|
intelResourceUpdateLock sync.Mutex
|
||||||
|
|||||||
Reference in New Issue
Block a user