Merge pull request #1125 from safing/feature/improve-update-system
Split auto update settings and add support for registry state
This commit is contained in:
@@ -127,7 +127,7 @@ func initCobra() {
|
||||
|
||||
// set up logging
|
||||
log.SetFlags(log.Ldate | log.Ltime | log.LUTC)
|
||||
log.SetPrefix("[control] ")
|
||||
log.SetPrefix("[pmstart] ")
|
||||
log.SetOutput(os.Stdout)
|
||||
|
||||
// not using portbase logger
|
||||
@@ -186,7 +186,7 @@ func ensureLoggingDir() error {
|
||||
|
||||
func updateRegistryIndex(mustLoadIndex bool) error {
|
||||
// Set indexes based on the release channel.
|
||||
warning := helper.SetIndexes(registry, "", false)
|
||||
warning := helper.SetIndexes(registry, "", false, false, false)
|
||||
if warning != nil {
|
||||
log.Printf("WARNING: %s\n", warning)
|
||||
}
|
||||
|
||||
@@ -3,11 +3,13 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
portlog "github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/updater"
|
||||
"github.com/safing/portmaster/updates/helper"
|
||||
)
|
||||
|
||||
@@ -58,23 +60,16 @@ func downloadUpdates() error {
|
||||
helper.IntelOnly()
|
||||
}
|
||||
|
||||
// Set registry state notify callback.
|
||||
registry.StateNotifyFunc = logProgress
|
||||
|
||||
// Set required updates.
|
||||
registry.MandatoryUpdates = helper.MandatoryUpdates()
|
||||
registry.AutoUnpack = helper.AutoUnpackUpdates()
|
||||
|
||||
// logging is configured as a persistent pre-run method inherited from
|
||||
// the root command but since we don't use run.Run() we need to start
|
||||
// logging ourself.
|
||||
log.SetLogLevel(log.InfoLevel)
|
||||
err := log.Start()
|
||||
if err != nil {
|
||||
fmt.Printf("failed to start logging: %s\n", err)
|
||||
}
|
||||
defer log.Shutdown()
|
||||
|
||||
if reset {
|
||||
// Delete storage.
|
||||
err = os.RemoveAll(registry.StorageDir().Path)
|
||||
err := os.RemoveAll(registry.StorageDir().Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to reset update dir: %w", err)
|
||||
}
|
||||
@@ -88,13 +83,19 @@ func downloadUpdates() error {
|
||||
}
|
||||
|
||||
// Update all indexes.
|
||||
err = registry.UpdateIndexes(context.TODO())
|
||||
err := registry.UpdateIndexes(context.TODO())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if updates are available.
|
||||
if len(registry.GetState().Updates.PendingDownload) == 0 {
|
||||
log.Println("all resources are up to date")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Download all required updates.
|
||||
err = registry.DownloadUpdates(context.TODO())
|
||||
err = registry.DownloadUpdates(context.TODO(), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -116,17 +117,43 @@ func downloadUpdates() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func logProgress(state *updater.RegistryState) {
|
||||
switch state.ID {
|
||||
case updater.StateChecking:
|
||||
if state.Updates.LastCheckAt == nil {
|
||||
log.Println("checking for new versions")
|
||||
}
|
||||
case updater.StateDownloading:
|
||||
if state.Details == nil {
|
||||
log.Printf("downloading %d updates\n", len(state.Updates.PendingDownload))
|
||||
} else if downloadDetails, ok := state.Details.(*updater.StateDownloadingDetails); ok {
|
||||
if downloadDetails.FinishedUpTo < len(downloadDetails.Resources) {
|
||||
log.Printf(
|
||||
"[%d/%d] downloading %s",
|
||||
downloadDetails.FinishedUpTo+1,
|
||||
len(downloadDetails.Resources),
|
||||
downloadDetails.Resources[downloadDetails.FinishedUpTo],
|
||||
)
|
||||
} else {
|
||||
if state.Updates.LastDownloadAt == nil {
|
||||
log.Println("finalizing downloads")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func purge() error {
|
||||
log.SetLogLevel(log.TraceLevel)
|
||||
portlog.SetLogLevel(portlog.TraceLevel)
|
||||
|
||||
// logging is configured as a persistent pre-run method inherited from
|
||||
// the root command but since we don't use run.Run() we need to start
|
||||
// logging ourself.
|
||||
err := log.Start()
|
||||
err := portlog.Start()
|
||||
if err != nil {
|
||||
fmt.Printf("failed to start logging: %s\n", err)
|
||||
}
|
||||
defer log.Shutdown()
|
||||
defer portlog.Shutdown()
|
||||
|
||||
registry.Purge(3)
|
||||
return nil
|
||||
|
||||
@@ -154,7 +154,7 @@ func verifyUpdates(ctx context.Context) error {
|
||||
// Re-download broken files.
|
||||
registry.MandatoryUpdates = helper.MandatoryUpdates()
|
||||
registry.AutoUnpack = helper.AutoUnpackUpdates()
|
||||
err = registry.DownloadUpdates(ctx)
|
||||
err = registry.DownloadUpdates(ctx, false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to re-download files: %w", err)
|
||||
}
|
||||
|
||||
11
go.mod
11
go.mod
@@ -17,11 +17,11 @@ require (
|
||||
github.com/miekg/dns v1.1.53
|
||||
github.com/oschwald/maxminddb-golang v1.10.0
|
||||
github.com/safing/jess v0.3.1
|
||||
github.com/safing/portbase v0.16.4
|
||||
github.com/safing/portbase v0.16.5
|
||||
github.com/safing/portmaster-android/go v0.0.0-20230329101752-28296331340b
|
||||
github.com/safing/spn v0.6.6
|
||||
github.com/safing/spn v0.6.7
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/spkg/zipfs v0.7.1
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/tannerryan/ring v1.1.2
|
||||
@@ -29,7 +29,7 @@ require (
|
||||
github.com/umahmood/haversine v0.0.0-20151105152445-808ab04add26
|
||||
golang.org/x/net v0.8.0
|
||||
golang.org/x/sync v0.1.0
|
||||
golang.org/x/sys v0.6.0
|
||||
golang.org/x/sys v0.7.0
|
||||
zombiezen.com/go/sqlite v0.13.0
|
||||
)
|
||||
|
||||
@@ -85,7 +85,8 @@ require (
|
||||
github.com/zalando/go-keyring v0.2.2 // indirect
|
||||
go.etcd.io/bbolt v1.3.7 // indirect
|
||||
golang.org/x/crypto v0.7.0 // indirect
|
||||
golang.org/x/mod v0.9.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
|
||||
golang.org/x/mod v0.10.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.7.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
|
||||
31
go.sum
31
go.sum
@@ -27,8 +27,8 @@ github.com/brianvoe/gofakeit v3.18.0+incompatible h1:wDOmHc9DLG4nRjUVVaxA+CEglKO
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
|
||||
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
@@ -48,8 +48,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8=
|
||||
github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE=
|
||||
github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI=
|
||||
github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=
|
||||
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
@@ -76,12 +76,12 @@ github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx
|
||||
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
|
||||
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
|
||||
github.com/golang/glog v1.1.1 h1:jxpi2eWoU84wbX9iIEyAeeoac3FLuifZpY9tcNUD9kw=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
@@ -201,12 +201,12 @@ github.com/safing/jess v0.3.1 h1:cMZVhi2whW/YdD98MPLeLIWJndQ7o2QVt2HefQ/ByFA=
|
||||
github.com/safing/jess v0.3.1/go.mod h1:aj73Eot1zm2ETkJuw9hJlIO8bRom52uBbsCHemvlZmA=
|
||||
github.com/safing/portbase v0.15.2/go.mod h1:5bHi99fz7Hh/wOsZUOI631WF9ePSHk57c4fdlOMS91Y=
|
||||
github.com/safing/portbase v0.16.2/go.mod h1:mzNCWqPbO7vIYbbK5PElGbudwd2vx4YPNawymL8Aro8=
|
||||
github.com/safing/portbase v0.16.4 h1:3w4Sc0TlSDvUS4Uk5TrsFXf98cwMbCf+BG5Bw6coxNk=
|
||||
github.com/safing/portbase v0.16.4/go.mod h1:mzNCWqPbO7vIYbbK5PElGbudwd2vx4YPNawymL8Aro8=
|
||||
github.com/safing/portbase v0.16.5 h1:vn6jGQW+ACnPVV57XtlNX+A/LESFh8/YmKn9rwAdy90=
|
||||
github.com/safing/portbase v0.16.5/go.mod h1:CJSY+0pgbgh00UcM9myS/l+Bcgqgc6afLJX2BqzD+Ek=
|
||||
github.com/safing/portmaster-android/go v0.0.0-20230329101752-28296331340b h1:b+GCsl+dTnR22JY3N2aRDVnac2ocjFqqUhn8rPxywGM=
|
||||
github.com/safing/portmaster-android/go v0.0.0-20230329101752-28296331340b/go.mod h1:E3MFiTwsHxsPfzI+CGhpE8BOGQYS/V7tkXttMTFeyuc=
|
||||
github.com/safing/spn v0.6.6 h1:hKSe0TmDRkyFKcSB/UejvWO+21BBzhbyEpjdw3Biq3w=
|
||||
github.com/safing/spn v0.6.6/go.mod h1:AnjcIlkmsMDzCf4APp5h45TsCjMxL5cEwlN5Pj+cMAs=
|
||||
github.com/safing/spn v0.6.7 h1:WNxzf6eEI/FyRtEjUwXK0GTwHD9VPxrKhlupLqQlz+E=
|
||||
github.com/safing/spn v0.6.7/go.mod h1:PsKnBKxCIYZncVk+rEWv67/PXcTfZJAUWbgj62hOYbw=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/seehuhn/fortuna v1.0.1 h1:lu9+CHsmR0bZnx5Ay646XvCSRJ8PJTi5UYJwDBX68H0=
|
||||
@@ -221,8 +221,8 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
|
||||
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
||||
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
@@ -301,13 +301,15 @@ golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0
|
||||
golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
|
||||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
|
||||
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -381,8 +383,9 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -408,7 +411,7 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d h1:qp0AnQCvRCMlu9jBjtdbTaaEmThIgZOrbVyDEOcmKhQ=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -26,7 +26,8 @@ func notifyOfNetworkChange() {
|
||||
module.TriggerEvent(NetworkChangedEvent, nil)
|
||||
}
|
||||
|
||||
func triggerNetworkChangeCheck() {
|
||||
// TriggerNetworkChangeCheck triggers a network change check.
|
||||
func TriggerNetworkChangeCheck() {
|
||||
select {
|
||||
case networkChangeCheckTrigger <- struct{}{}:
|
||||
default:
|
||||
|
||||
@@ -209,7 +209,7 @@ func updateOnlineStatus(status OnlineStatus, portalURL *url.URL, comment string)
|
||||
} else {
|
||||
log.Infof("netenv: setting online status to %s (%s)", status, comment)
|
||||
}
|
||||
triggerNetworkChangeCheck()
|
||||
TriggerNetworkChangeCheck()
|
||||
|
||||
// Notify user.
|
||||
notifyOnlineStatus(status)
|
||||
|
||||
@@ -170,19 +170,18 @@ func GetOrFindProcess(ctx context.Context, pid int) (*Process, error) {
|
||||
return GetSystemProcess(ctx), nil
|
||||
}
|
||||
|
||||
// Get pid and created time for identification.
|
||||
// Get pid and creation time for identification.
|
||||
pInfo, err := processInfo.NewProcessWithContext(ctx, int32(pid))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
createdTime, err := pInfo.CreateTimeWithContext(ctx)
|
||||
createdAt, err := pInfo.CreateTimeWithContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := getProcessKey(int32(pid), createdAt)
|
||||
|
||||
key := getProcessKey(int32(pid), createdTime)
|
||||
|
||||
// Load process and make sure it is only loaded once.
|
||||
p, err, _ := getProcessSingleInflight.Do(key, func() (interface{}, error) {
|
||||
return loadProcess(ctx, key, pInfo)
|
||||
})
|
||||
@@ -197,9 +196,7 @@ func GetOrFindProcess(ctx context.Context, pid int) (*Process, error) {
|
||||
}
|
||||
|
||||
func loadProcess(ctx context.Context, key string, pInfo *processInfo.Process) (*Process, error) {
|
||||
// Get created time of process. The value should be cached.
|
||||
createdAt, _ := pInfo.CreateTimeWithContext(ctx)
|
||||
|
||||
// Check if we already have the process.
|
||||
process, ok := GetProcessFromStorage(key)
|
||||
if ok {
|
||||
return process, nil
|
||||
@@ -208,13 +205,13 @@ func loadProcess(ctx context.Context, key string, pInfo *processInfo.Process) (*
|
||||
// Create new a process object.
|
||||
process = &Process{
|
||||
Pid: int(pInfo.Pid),
|
||||
CreatedAt: createdAt,
|
||||
FirstSeen: time.Now().Unix(),
|
||||
processKey: key,
|
||||
}
|
||||
|
||||
// Get process information from the system.
|
||||
pInfo, err := processInfo.NewProcessWithContext(ctx, pInfo.Pid)
|
||||
// Get creation time of process. (The value should be cached by the library.)
|
||||
var err error
|
||||
process.CreatedAt, err = pInfo.CreateTimeWithContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -38,35 +38,32 @@ func init() {
|
||||
var (
|
||||
// unidentifiedProcess is used for non-attributed outgoing connections.
|
||||
unidentifiedProcess = &Process{
|
||||
UserID: UnidentifiedProcessID,
|
||||
UserName: "Unknown",
|
||||
Pid: UnidentifiedProcessID,
|
||||
CreatedAt: 1,
|
||||
ParentPid: UnidentifiedProcessID,
|
||||
ParentCreatedAt: 1,
|
||||
Name: profile.UnidentifiedProfileName,
|
||||
UserID: UnidentifiedProcessID,
|
||||
UserName: "Unknown",
|
||||
Pid: UnidentifiedProcessID,
|
||||
ParentPid: UnidentifiedProcessID,
|
||||
Name: profile.UnidentifiedProfileName,
|
||||
processKey: getProcessKey(UnidentifiedProcessID, 0),
|
||||
}
|
||||
|
||||
// unsolicitedProcess is used for non-attributed incoming connections.
|
||||
unsolicitedProcess = &Process{
|
||||
UserID: UnsolicitedProcessID,
|
||||
UserName: "Unknown",
|
||||
Pid: UnsolicitedProcessID,
|
||||
CreatedAt: 1,
|
||||
ParentPid: UnsolicitedProcessID,
|
||||
ParentCreatedAt: 1,
|
||||
Name: profile.UnsolicitedProfileName,
|
||||
UserID: UnsolicitedProcessID,
|
||||
UserName: "Unknown",
|
||||
Pid: UnsolicitedProcessID,
|
||||
ParentPid: UnsolicitedProcessID,
|
||||
Name: profile.UnsolicitedProfileName,
|
||||
processKey: getProcessKey(UnsolicitedProcessID, 0),
|
||||
}
|
||||
|
||||
// systemProcess is used to represent the Kernel.
|
||||
systemProcess = &Process{
|
||||
UserID: SystemProcessID,
|
||||
UserName: "Kernel",
|
||||
Pid: SystemProcessID,
|
||||
CreatedAt: 1,
|
||||
ParentPid: SystemProcessID,
|
||||
ParentCreatedAt: 1,
|
||||
Name: profile.SystemProfileName,
|
||||
UserID: SystemProcessID,
|
||||
UserName: "Kernel",
|
||||
Pid: SystemProcessID,
|
||||
ParentPid: SystemProcessID,
|
||||
Name: profile.SystemProfileName,
|
||||
processKey: getProcessKey(SystemProcessID, 0),
|
||||
}
|
||||
|
||||
getSpecialProcessSingleInflight singleflight.Group
|
||||
|
||||
@@ -7,25 +7,24 @@ import (
|
||||
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/notifications"
|
||||
"github.com/safing/portmaster/updates/helper"
|
||||
)
|
||||
|
||||
const (
|
||||
cfgDevModeKey = "core/devMode"
|
||||
updatesDisabledNotificationID = "updates:disabled"
|
||||
)
|
||||
const cfgDevModeKey = "core/devMode"
|
||||
|
||||
var (
|
||||
releaseChannel config.StringOption
|
||||
devMode config.BoolOption
|
||||
enableUpdates config.BoolOption
|
||||
releaseChannel config.StringOption
|
||||
devMode config.BoolOption
|
||||
enableSoftwareUpdates config.BoolOption
|
||||
enableIntelUpdates config.BoolOption
|
||||
|
||||
initialReleaseChannel string
|
||||
previousReleaseChannel string
|
||||
updatesCurrentlyEnabled bool
|
||||
previousDevMode bool
|
||||
forceUpdate = abool.New()
|
||||
initialReleaseChannel string
|
||||
previousReleaseChannel string
|
||||
|
||||
softwareUpdatesCurrentlyEnabled bool
|
||||
intelUpdatesCurrentlyEnabled bool
|
||||
previousDevMode bool
|
||||
forceUpdate = abool.New()
|
||||
)
|
||||
|
||||
func registerConfig() error {
|
||||
@@ -71,9 +70,9 @@ func registerConfig() error {
|
||||
}
|
||||
|
||||
err = config.Register(&config.Option{
|
||||
Name: "Automatic Updates",
|
||||
Key: enableUpdatesKey,
|
||||
Description: "Enable automatic checking, downloading and applying of updates. This affects all kinds of updates, including intelligence feeds and broadcast notifications.",
|
||||
Name: "Automatic Software Updates",
|
||||
Key: enableSoftwareUpdatesKey,
|
||||
Description: "Automatically check for and download software updates. This does not include intelligence data updates.",
|
||||
OptType: config.OptTypeBool,
|
||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||
ReleaseLevel: config.ReleaseLevelStable,
|
||||
@@ -88,6 +87,24 @@ func registerConfig() error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = config.Register(&config.Option{
|
||||
Name: "Automatic Intelligence Data Updates",
|
||||
Key: enableIntelUpdatesKey,
|
||||
Description: "Automatically check for and download intelligence data updates. This includes filter lists, geo-ip data, and more. Does not include software updates.",
|
||||
OptType: config.OptTypeBool,
|
||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||
ReleaseLevel: config.ReleaseLevelStable,
|
||||
RequiresRestart: false,
|
||||
DefaultValue: true,
|
||||
Annotations: config.Annotations{
|
||||
config.DisplayOrderAnnotation: -11,
|
||||
config.CategoryAnnotation: "Updates",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -96,37 +113,25 @@ func initConfig() {
|
||||
initialReleaseChannel = releaseChannel()
|
||||
previousReleaseChannel = releaseChannel()
|
||||
|
||||
enableUpdates = config.Concurrent.GetAsBool(enableUpdatesKey, true)
|
||||
updatesCurrentlyEnabled = enableUpdates()
|
||||
enableSoftwareUpdates = config.Concurrent.GetAsBool(enableSoftwareUpdatesKey, true)
|
||||
enableIntelUpdates = config.Concurrent.GetAsBool(enableIntelUpdatesKey, true)
|
||||
softwareUpdatesCurrentlyEnabled = enableSoftwareUpdates()
|
||||
intelUpdatesCurrentlyEnabled = enableIntelUpdates()
|
||||
|
||||
devMode = config.Concurrent.GetAsBool(cfgDevModeKey, false)
|
||||
previousDevMode = devMode()
|
||||
}
|
||||
|
||||
func createWarningNotification() {
|
||||
notifications.NotifyWarn(
|
||||
updatesDisabledNotificationID,
|
||||
"Automatic Updates Disabled",
|
||||
"Automatic updates are disabled through configuration. Please note that this is potentially dangerous, as this also affects security updates as well as the filter lists and threat intelligence feeds.",
|
||||
notifications.Action{
|
||||
Text: "Change",
|
||||
Type: notifications.ActionTypeOpenSetting,
|
||||
Payload: ¬ifications.ActionTypeOpenSettingPayload{
|
||||
Key: enableUpdatesKey,
|
||||
},
|
||||
},
|
||||
).AttachToModule(module)
|
||||
}
|
||||
|
||||
func updateRegistryConfig(_ context.Context, _ interface{}) error {
|
||||
changed := false
|
||||
|
||||
if releaseChannel() != previousReleaseChannel {
|
||||
previousReleaseChannel = releaseChannel()
|
||||
warning := helper.SetIndexes(registry, releaseChannel(), true)
|
||||
if warning != nil {
|
||||
log.Warningf("updates: %s", warning)
|
||||
}
|
||||
if enableSoftwareUpdates() != softwareUpdatesCurrentlyEnabled {
|
||||
softwareUpdatesCurrentlyEnabled = enableSoftwareUpdates()
|
||||
changed = true
|
||||
}
|
||||
|
||||
if enableIntelUpdates() != intelUpdatesCurrentlyEnabled {
|
||||
intelUpdatesCurrentlyEnabled = enableIntelUpdates()
|
||||
changed = true
|
||||
}
|
||||
|
||||
@@ -136,24 +141,36 @@ func updateRegistryConfig(_ context.Context, _ interface{}) error {
|
||||
changed = true
|
||||
}
|
||||
|
||||
if enableUpdates() != updatesCurrentlyEnabled {
|
||||
updatesCurrentlyEnabled = enableUpdates()
|
||||
if releaseChannel() != previousReleaseChannel {
|
||||
previousReleaseChannel = releaseChannel()
|
||||
changed = true
|
||||
}
|
||||
|
||||
if changed {
|
||||
// Update indexes based on new settings.
|
||||
warning := helper.SetIndexes(
|
||||
registry,
|
||||
releaseChannel(),
|
||||
true,
|
||||
softwareUpdatesCurrentlyEnabled,
|
||||
intelUpdatesCurrentlyEnabled,
|
||||
)
|
||||
if warning != nil {
|
||||
log.Warningf("updates: %s", warning)
|
||||
}
|
||||
|
||||
// Select versions depending on new indexes and modes.
|
||||
registry.SelectVersions()
|
||||
module.TriggerEvent(VersionUpdateEvent, nil)
|
||||
|
||||
if updatesCurrentlyEnabled {
|
||||
if softwareUpdatesCurrentlyEnabled || intelUpdatesCurrentlyEnabled {
|
||||
module.Resolve("")
|
||||
if err := TriggerUpdate(false); err != nil {
|
||||
log.Warningf("updates: failed to trigger update: %s", err)
|
||||
}
|
||||
log.Infof("updates: automatic updates are now enabled")
|
||||
} else {
|
||||
createWarningNotification()
|
||||
log.Warningf("updates: automatic updates are now disabled")
|
||||
log.Warningf("updates: automatic updates are now completely disabled")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,9 @@ const (
|
||||
|
||||
// versionsDBKey is the database key for simple update version information.
|
||||
simpleVersionsDBKey = "core:status/simple-versions"
|
||||
|
||||
// updateStatusDBKey is the database key for update status information.
|
||||
updateStatusDBKey = "core:status/updates"
|
||||
)
|
||||
|
||||
// Versions holds update versions and status information.
|
||||
@@ -50,6 +53,14 @@ type SimplifiedResourceVersion struct {
|
||||
Version string
|
||||
}
|
||||
|
||||
// UpdateStateExport is a wrapper to export the updates state.
|
||||
type UpdateStateExport struct {
|
||||
record.Base
|
||||
sync.Mutex
|
||||
|
||||
*updater.UpdateState
|
||||
}
|
||||
|
||||
// GetVersions returns the update versions and status information.
|
||||
// Resources must be locked when accessed.
|
||||
func GetVersions() *Versions {
|
||||
@@ -98,6 +109,41 @@ func GetSimpleVersions() *SimpleVersions {
|
||||
return v
|
||||
}
|
||||
|
||||
// GetStateExport gets the update state from the registry and returns it in an
|
||||
// exportable struct.
|
||||
func GetStateExport() *UpdateStateExport {
|
||||
export := registry.GetState()
|
||||
return &UpdateStateExport{
|
||||
UpdateState: &export.Updates,
|
||||
}
|
||||
}
|
||||
|
||||
// LoadStateExport loads the exported update state from the database.
|
||||
func LoadStateExport() (*UpdateStateExport, error) {
|
||||
r, err := db.Get(updateStatusDBKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// unwrap
|
||||
if r.IsWrapped() {
|
||||
// only allocate a new struct, if we need it
|
||||
newRecord := &UpdateStateExport{}
|
||||
err = record.Unwrap(r, newRecord)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newRecord, nil
|
||||
}
|
||||
|
||||
// or adjust type
|
||||
newRecord, ok := r.(*UpdateStateExport)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("record not of type *UpdateStateExport, but %T", r)
|
||||
}
|
||||
return newRecord, nil
|
||||
}
|
||||
|
||||
func initVersionExport() (err error) {
|
||||
if err := GetVersions().save(); err != nil {
|
||||
log.Warningf("updates: failed to export version information: %s", err)
|
||||
@@ -128,12 +174,28 @@ func (v *SimpleVersions) save() error {
|
||||
return db.Put(v)
|
||||
}
|
||||
|
||||
func (s *UpdateStateExport) save() error {
|
||||
if !s.KeyIsSet() {
|
||||
s.SetKey(updateStatusDBKey)
|
||||
}
|
||||
return db.Put(s)
|
||||
}
|
||||
|
||||
// export is an event hook.
|
||||
func export(_ context.Context, _ interface{}) error {
|
||||
// Export versions.
|
||||
if err := GetVersions().save(); err != nil {
|
||||
return err
|
||||
}
|
||||
return GetSimpleVersions().save()
|
||||
if err := GetSimpleVersions().save(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Export udpate state.
|
||||
if err := GetStateExport().save(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddToDebugInfo adds the update system status to the given debug.Info.
|
||||
|
||||
@@ -32,3 +32,30 @@ func GetFile(identifier string) (*updater.File, error) {
|
||||
module.TriggerEvent(VersionUpdateEvent, nil)
|
||||
return file, nil
|
||||
}
|
||||
|
||||
// GetPlatformVersion returns the selected platform specific version of the
|
||||
// given identifier.
|
||||
// The returned resource version may not be modified.
|
||||
func GetPlatformVersion(identifier string) (*updater.ResourceVersion, error) {
|
||||
identifier = helper.PlatformIdentifier(identifier)
|
||||
|
||||
rv, err := registry.GetVersion(identifier)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
// GetVersion returns the selected generic version of the given identifier.
|
||||
// The returned resource version may not be modified.
|
||||
func GetVersion(identifier string) (*updater.ResourceVersion, error) {
|
||||
identifier = path.Join("all", identifier)
|
||||
|
||||
rv, err := registry.GetVersion(identifier)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
@@ -27,7 +27,13 @@ const (
|
||||
|
||||
// SetIndexes sets the update registry indexes and also configures the registry
|
||||
// to use pre-releases based on the channel.
|
||||
func SetIndexes(registry *updater.ResourceRegistry, releaseChannel string, deleteUnusedIndexes bool) (warning error) {
|
||||
func SetIndexes(
|
||||
registry *updater.ResourceRegistry,
|
||||
releaseChannel string,
|
||||
deleteUnusedIndexes bool,
|
||||
autoDownload bool,
|
||||
autoDownloadIntel bool,
|
||||
) (warning error) {
|
||||
usePreReleases := false
|
||||
|
||||
// Be reminded that the order is important, as indexes added later will
|
||||
@@ -39,12 +45,14 @@ func SetIndexes(registry *updater.ResourceRegistry, releaseChannel string, delet
|
||||
// Add the intel index first, in order to be able to override it with the
|
||||
// other indexes when needed.
|
||||
registry.AddIndex(updater.Index{
|
||||
Path: "all/intel/intel.json",
|
||||
Path: "all/intel/intel.json",
|
||||
AutoDownload: autoDownloadIntel,
|
||||
})
|
||||
|
||||
// Always add the stable index as a base.
|
||||
registry.AddIndex(updater.Index{
|
||||
Path: ReleaseChannelStable + ".json",
|
||||
Path: ReleaseChannelStable + ".json",
|
||||
AutoDownload: autoDownload,
|
||||
})
|
||||
|
||||
// Add beta index if in beta or staging channel.
|
||||
@@ -53,8 +61,9 @@ func SetIndexes(registry *updater.ResourceRegistry, releaseChannel string, delet
|
||||
releaseChannel == ReleaseChannelStaging ||
|
||||
(releaseChannel == "" && indexExists(registry, indexPath)) {
|
||||
registry.AddIndex(updater.Index{
|
||||
Path: indexPath,
|
||||
PreRelease: true,
|
||||
Path: indexPath,
|
||||
PreRelease: true,
|
||||
AutoDownload: autoDownload,
|
||||
})
|
||||
usePreReleases = true
|
||||
} else if deleteUnusedIndexes {
|
||||
@@ -69,8 +78,9 @@ func SetIndexes(registry *updater.ResourceRegistry, releaseChannel string, delet
|
||||
if releaseChannel == ReleaseChannelStaging ||
|
||||
(releaseChannel == "" && indexExists(registry, indexPath)) {
|
||||
registry.AddIndex(updater.Index{
|
||||
Path: indexPath,
|
||||
PreRelease: true,
|
||||
Path: indexPath,
|
||||
PreRelease: true,
|
||||
AutoDownload: autoDownload,
|
||||
})
|
||||
usePreReleases = true
|
||||
} else if deleteUnusedIndexes {
|
||||
@@ -85,7 +95,8 @@ func SetIndexes(registry *updater.ResourceRegistry, releaseChannel string, delet
|
||||
if releaseChannel == ReleaseChannelSupport ||
|
||||
(releaseChannel == "" && indexExists(registry, indexPath)) {
|
||||
registry.AddIndex(updater.Index{
|
||||
Path: indexPath,
|
||||
Path: indexPath,
|
||||
AutoDownload: autoDownload,
|
||||
})
|
||||
} else if deleteUnusedIndexes {
|
||||
err := deleteIndex(registry, indexPath)
|
||||
|
||||
@@ -5,14 +5,12 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/database"
|
||||
"github.com/safing/portbase/dataroot"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portbase/notifications"
|
||||
"github.com/safing/portbase/updater"
|
||||
"github.com/safing/portmaster/updates/helper"
|
||||
)
|
||||
@@ -20,7 +18,8 @@ import (
|
||||
const (
|
||||
onWindows = runtime.GOOS == "windows"
|
||||
|
||||
enableUpdatesKey = "core/automaticUpdates"
|
||||
enableSoftwareUpdatesKey = "core/automaticUpdates"
|
||||
enableIntelUpdatesKey = "core/automaticIntelUpdates"
|
||||
|
||||
// ModuleName is the name of the update module
|
||||
// and can be used when declaring module dependencies.
|
||||
@@ -64,9 +63,6 @@ var (
|
||||
const (
|
||||
updatesDirName = "updates"
|
||||
|
||||
updateFailed = "updates:failed"
|
||||
updateSuccess = "updates:success"
|
||||
|
||||
updateTaskRepeatDuration = 1 * time.Hour
|
||||
)
|
||||
|
||||
@@ -119,14 +115,39 @@ func start() error {
|
||||
// override with flag value
|
||||
registry.UserAgent = userAgentFromFlag
|
||||
}
|
||||
|
||||
// pre-init state
|
||||
updateStateExport, err := LoadStateExport()
|
||||
if err != nil {
|
||||
log.Debugf("updates: failed to load exported update state: %s", err)
|
||||
} else if updateStateExport.UpdateState != nil {
|
||||
err := registry.PreInitUpdateState(*updateStateExport.UpdateState)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// initialize
|
||||
err := registry.Initialize(dataroot.Root().ChildDir(updatesDirName, 0o0755))
|
||||
err = registry.Initialize(dataroot.Root().ChildDir(updatesDirName, 0o0755))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// register state provider
|
||||
err = registerRegistryStateProvider()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
registry.StateNotifyFunc = pushRegistryState
|
||||
|
||||
// Set indexes based on the release channel.
|
||||
warning := helper.SetIndexes(registry, initialReleaseChannel, true)
|
||||
warning := helper.SetIndexes(
|
||||
registry,
|
||||
initialReleaseChannel,
|
||||
true,
|
||||
enableSoftwareUpdates(),
|
||||
enableIntelUpdates(),
|
||||
)
|
||||
if warning != nil {
|
||||
log.Warningf("updates: %s", warning)
|
||||
}
|
||||
@@ -144,10 +165,6 @@ func start() error {
|
||||
registry.SelectVersions()
|
||||
module.TriggerEvent(VersionUpdateEvent, nil)
|
||||
|
||||
if !updatesCurrentlyEnabled {
|
||||
createWarningNotification()
|
||||
}
|
||||
|
||||
// Initialize the version export - this requires the registry to be set up.
|
||||
err = initVersionExport()
|
||||
if err != nil {
|
||||
@@ -185,11 +202,13 @@ func TriggerUpdate(force bool) error {
|
||||
case !module.Online():
|
||||
updateASAP = true
|
||||
|
||||
case !force && !enableUpdates():
|
||||
case !force && !enableSoftwareUpdates() && !enableIntelUpdates():
|
||||
return fmt.Errorf("automatic updating is disabled")
|
||||
|
||||
default:
|
||||
forceUpdate.Set()
|
||||
if force {
|
||||
forceUpdate.Set()
|
||||
}
|
||||
updateTask.StartASAP()
|
||||
}
|
||||
|
||||
@@ -211,8 +230,6 @@ func DisableUpdateSchedule() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var updateFailedCnt = new(atomic.Int32)
|
||||
|
||||
func checkForUpdates(ctx context.Context) (err error) {
|
||||
// Set correct error if context was canceled.
|
||||
defer func() {
|
||||
@@ -223,7 +240,8 @@ func checkForUpdates(ctx context.Context) (err error) {
|
||||
}
|
||||
}()
|
||||
|
||||
if !forceUpdate.SetToIf(true, false) && !enableUpdates() {
|
||||
forcedUpdate := forceUpdate.SetToIf(true, false)
|
||||
if !forcedUpdate && !enableSoftwareUpdates() && !enableIntelUpdates() {
|
||||
log.Warningf("updates: automatic updates are disabled")
|
||||
return nil
|
||||
}
|
||||
@@ -231,45 +249,14 @@ func checkForUpdates(ctx context.Context) (err error) {
|
||||
defer func() {
|
||||
// Resolve any error and and send succes notification.
|
||||
if err == nil {
|
||||
updateFailedCnt.Store(0)
|
||||
log.Infof("updates: successfully checked for updates")
|
||||
module.Resolve(updateFailed)
|
||||
notifications.Notify(¬ifications.Notification{
|
||||
EventID: updateSuccess,
|
||||
Type: notifications.Info,
|
||||
Title: "Update Check Successful",
|
||||
Message: "The Portmaster successfully checked for updates and downloaded any available updates. Most updates are applied automatically. You will be notified of important updates that need restarting.",
|
||||
Expires: time.Now().Add(1 * time.Minute).Unix(),
|
||||
AvailableActions: []*notifications.Action{
|
||||
{
|
||||
ID: "ack",
|
||||
Text: "OK",
|
||||
},
|
||||
},
|
||||
})
|
||||
notifyUpdateSuccess(forcedUpdate)
|
||||
return
|
||||
}
|
||||
|
||||
// Log error in any case.
|
||||
// Log and notify error.
|
||||
log.Errorf("updates: check failed: %s", err)
|
||||
|
||||
// Do not alert user if update failed for only a few times.
|
||||
if updateFailedCnt.Add(1) > 3 {
|
||||
notifications.NotifyWarn(
|
||||
updateFailed,
|
||||
"Update Check Failed",
|
||||
"The Portmaster failed to check for updates. This might be a temporary issue of your device, your network or the update servers. The Portmaster will automatically try again later. If you just installed the Portmaster, please try disabling potentially conflicting software, such as other firewalls or VPNs.",
|
||||
notifications.Action{
|
||||
ID: "retry",
|
||||
Text: "Try Again Now",
|
||||
Type: notifications.ActionTypeWebhook,
|
||||
Payload: ¬ifications.ActionTypeWebhookPayload{
|
||||
URL: apiPathCheckForUpdates,
|
||||
ResultAction: "display",
|
||||
},
|
||||
},
|
||||
).AttachToModule(module)
|
||||
}
|
||||
notifyUpdateCheckFailed(forcedUpdate, err)
|
||||
}()
|
||||
|
||||
if err = registry.UpdateIndexes(ctx); err != nil {
|
||||
@@ -277,7 +264,7 @@ func checkForUpdates(ctx context.Context) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
err = registry.DownloadUpdates(ctx)
|
||||
err = registry.DownloadUpdates(ctx, !forcedUpdate)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to download updates: %w", err)
|
||||
return
|
||||
@@ -293,7 +280,7 @@ func checkForUpdates(ctx context.Context) (err error) {
|
||||
}
|
||||
|
||||
// Purge old resources
|
||||
registry.Purge(3)
|
||||
registry.Purge(2)
|
||||
|
||||
module.TriggerEvent(ResourceUpdateEvent, nil)
|
||||
return nil
|
||||
|
||||
140
updates/notify.go
Normal file
140
updates/notify.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package updates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/notifications"
|
||||
)
|
||||
|
||||
const (
|
||||
updateFailed = "updates:failed"
|
||||
updateSuccess = "updates:success"
|
||||
updateSuccessPending = "updates:success-pending"
|
||||
updateSuccessDownloaded = "updates:success-downloaded"
|
||||
|
||||
failedUpdateNotifyDurationThreshold = 24 * time.Hour
|
||||
failedUpdateNotifyCountThreshold = 3
|
||||
)
|
||||
|
||||
var updateFailedCnt = new(atomic.Int32)
|
||||
|
||||
func notifyUpdateSuccess(forced bool) {
|
||||
updateFailedCnt.Store(0)
|
||||
module.Resolve(updateFailed)
|
||||
updateState := registry.GetState().Updates
|
||||
|
||||
flavor := updateSuccess
|
||||
switch {
|
||||
case len(updateState.PendingDownload) > 0:
|
||||
// Show notification if there are pending downloads.
|
||||
flavor = updateSuccessPending
|
||||
case updateState.LastDownloadAt != nil &&
|
||||
time.Since(*updateState.LastDownloadAt) < time.Minute:
|
||||
// Show notification if we downloaded something within the last minute.
|
||||
flavor = updateSuccessDownloaded
|
||||
case forced:
|
||||
// Always show notification if update was manually triggered.
|
||||
default:
|
||||
// Otherwise, the update was uneventful. Do not show notification.
|
||||
return
|
||||
}
|
||||
|
||||
switch flavor {
|
||||
case updateSuccess:
|
||||
notifications.Notify(¬ifications.Notification{
|
||||
EventID: updateSuccess,
|
||||
Type: notifications.Info,
|
||||
Title: "Portmaster Is Up-To-Date",
|
||||
Message: "Portmaster successfully checked for updates. Everything is up to date. Most updates are applied automatically. You will be notified of important updates that need restarting.",
|
||||
Expires: time.Now().Add(1 * time.Minute).Unix(),
|
||||
AvailableActions: []*notifications.Action{
|
||||
{
|
||||
ID: "ack",
|
||||
Text: "OK",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
case updateSuccessPending:
|
||||
notifications.Notify(¬ifications.Notification{
|
||||
EventID: updateSuccess,
|
||||
Type: notifications.Info,
|
||||
Title: fmt.Sprintf("%d Updates Available", len(updateState.PendingDownload)),
|
||||
Message: fmt.Sprintf(
|
||||
`%d updates are available for download. Press "Download Now" or check for updates later to download and automatically apply all pending updates. You will be notified of important updates that need restarting.`,
|
||||
len(updateState.PendingDownload),
|
||||
),
|
||||
AvailableActions: []*notifications.Action{
|
||||
{
|
||||
ID: "ack",
|
||||
Text: "OK",
|
||||
},
|
||||
{
|
||||
ID: "download",
|
||||
Text: "Download Now",
|
||||
Type: notifications.ActionTypeWebhook,
|
||||
Payload: ¬ifications.ActionTypeWebhookPayload{
|
||||
URL: apiPathCheckForUpdates,
|
||||
ResultAction: "display",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
case updateSuccessDownloaded:
|
||||
notifications.Notify(¬ifications.Notification{
|
||||
EventID: updateSuccess,
|
||||
Type: notifications.Info,
|
||||
Title: fmt.Sprintf("%d Updates Applied", len(updateState.LastDownload)),
|
||||
Message: fmt.Sprintf(
|
||||
`%d updates were downloaded and applied. You will be notified of important updates that need restarting.`,
|
||||
len(updateState.LastDownload),
|
||||
),
|
||||
Expires: time.Now().Add(1 * time.Minute).Unix(),
|
||||
AvailableActions: []*notifications.Action{
|
||||
{
|
||||
ID: "ack",
|
||||
Text: "OK",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func notifyUpdateCheckFailed(forced bool, err error) {
|
||||
failedCnt := updateFailedCnt.Add(1)
|
||||
lastSuccess := registry.GetState().Updates.LastSuccessAt
|
||||
|
||||
switch {
|
||||
case forced:
|
||||
// Always show notification if update was manually triggered.
|
||||
case failedCnt < failedUpdateNotifyCountThreshold:
|
||||
// Not failed often enough for notification.
|
||||
return
|
||||
case lastSuccess == nil:
|
||||
// No recorded successful udpate.
|
||||
case time.Now().Add(-failedUpdateNotifyDurationThreshold).Before(*lastSuccess):
|
||||
// Failed too recently for notification.
|
||||
return
|
||||
}
|
||||
|
||||
notifications.NotifyWarn(
|
||||
updateFailed,
|
||||
"Update Check Failed",
|
||||
fmt.Sprintf(
|
||||
"Portmaster failed to check for updates. This might be a temporary issue of your device, your network or the update servers. The Portmaster will automatically try again later. The error was: %s",
|
||||
err,
|
||||
),
|
||||
notifications.Action{
|
||||
Text: "Try Again Now",
|
||||
Type: notifications.ActionTypeWebhook,
|
||||
Payload: ¬ifications.ActionTypeWebhookPayload{
|
||||
URL: apiPathCheckForUpdates,
|
||||
ResultAction: "display",
|
||||
},
|
||||
},
|
||||
).AttachToModule(module)
|
||||
}
|
||||
49
updates/state.go
Normal file
49
updates/state.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package updates
|
||||
|
||||
import (
|
||||
"github.com/safing/portbase/database/record"
|
||||
"github.com/safing/portbase/runtime"
|
||||
"github.com/safing/portbase/updater"
|
||||
)
|
||||
|
||||
var pushRegistryStatusUpdate runtime.PushFunc
|
||||
|
||||
// RegistryStateExport is a wrapper to export the registry state.
|
||||
type RegistryStateExport struct {
|
||||
record.Base
|
||||
*updater.RegistryState
|
||||
}
|
||||
|
||||
func exportRegistryState(s *updater.RegistryState) *RegistryStateExport {
|
||||
if s == nil {
|
||||
state := registry.GetState()
|
||||
s = &state
|
||||
}
|
||||
|
||||
export := &RegistryStateExport{
|
||||
RegistryState: s,
|
||||
}
|
||||
|
||||
export.CreateMeta()
|
||||
export.SetKey("runtime:core/updates/state")
|
||||
|
||||
return export
|
||||
}
|
||||
|
||||
func pushRegistryState(s *updater.RegistryState) {
|
||||
export := exportRegistryState(s)
|
||||
pushRegistryStatusUpdate(export)
|
||||
}
|
||||
|
||||
func registerRegistryStateProvider() (err error) {
|
||||
registryStateProvider := runtime.SimpleValueGetterFunc(func(_ string) ([]record.Record, error) {
|
||||
return []record.Record{exportRegistryState(nil)}, nil
|
||||
})
|
||||
|
||||
pushRegistryStatusUpdate, err = runtime.Register("core/updates/state", registryStateProvider)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user