Restructure modules (#1572)

* Move portbase into monorepo

* Add new simple module mgr

* [WIP] Switch to new simple module mgr

* Add StateMgr and more worker variants

* [WIP] Switch more modules

* [WIP] Switch more modules

* [WIP] swtich more modules

* [WIP] switch all SPN modules

* [WIP] switch all service modules

* [WIP] Convert all workers to the new module system

* [WIP] add new task system to module manager

* [WIP] Add second take for scheduling workers

* [WIP] Add FIXME for bugs in new scheduler

* [WIP] Add minor improvements to scheduler

* [WIP] Add new worker scheduler

* [WIP] Fix more bug related to new module system

* [WIP] Fix start handing of the new module system

* [WIP] Improve startup process

* [WIP] Fix minor issues

* [WIP] Fix missing subsystem in settings

* [WIP] Initialize managers in constructor

* [WIP] Move module event initialization to constrictors

* [WIP] Fix setting for enabling and disabling the SPN module

* [WIP] Move API registeration into module construction

* [WIP] Update states mgr for all modules

* [WIP] Add CmdLine operation support

* Add state helper methods to module group and instance

* Add notification and module status handling to status package

* Fix starting issues

* Remove pilot widget and update security lock to new status data

* Remove debug logs

* Improve http server shutdown

* Add workaround for cleanly shutting down firewall+netquery

* Improve logging

* Add syncing states with notifications for new module system

* Improve starting, stopping, shutdown; resolve FIXMEs/TODOs

* [WIP] Fix most unit tests

* Review new module system and fix minor issues

* Push shutdown and restart events again via API

* Set sleep mode via interface

* Update example/template module

* [WIP] Fix spn/cabin unit test

* Remove deprecated UI elements

* Make log output more similar for the logging transition phase

* Switch spn hub and observer cmds to new module system

* Fix log sources

* Make worker mgr less error prone

* Fix tests and minor issues

* Fix observation hub

* Improve shutdown and restart handling

* Split up big connection.go source file

* Move varint and dsd packages to structures repo

* Improve expansion test

* Fix linter warnings

* Fix interception module on windows

* Fix linter errors

---------

Co-authored-by: Vladimir Stoilov <vladimir@safing.io>
This commit is contained in:
Daniel Hååvi
2024-08-09 17:15:48 +02:00
committed by GitHub
parent 10a77498f4
commit 80664d1a27
647 changed files with 37690 additions and 3366 deletions

View File

@@ -56,5 +56,5 @@ echo "This information is useful for debugging and license compliance."
echo "Run the compiled binary with the -version flag to see the information included."
# build
BUILD_PATH="github.com/safing/portbase/info"
BUILD_PATH="github.com/safing/portmaster/base/info"
go build $DEV -ldflags "-X ${BUILD_PATH}.commit=${BUILD_COMMIT} -X ${BUILD_PATH}.buildOptions=${BUILD_BUILDOPTIONS} -X ${BUILD_PATH}.buildUser=${BUILD_USER} -X ${BUILD_PATH}.buildHost=${BUILD_HOST} -X ${BUILD_PATH}.buildDate=${BUILD_DATE} -X ${BUILD_PATH}.buildSource=${BUILD_SOURCE}" $*

View File

@@ -1,20 +1,25 @@
package main
import (
"errors"
"flag"
"fmt"
"io"
"log/slog"
"os"
"os/signal"
"runtime"
"runtime/pprof"
"syscall"
"time"
"github.com/safing/portbase/info"
"github.com/safing/portbase/metrics"
"github.com/safing/portbase/modules"
"github.com/safing/portbase/run"
_ "github.com/safing/portmaster/service/core/base"
_ "github.com/safing/portmaster/service/ui"
"github.com/safing/portmaster/base/info"
"github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/base/metrics"
"github.com/safing/portmaster/service/mgr"
"github.com/safing/portmaster/service/updates"
"github.com/safing/portmaster/service/updates/helper"
_ "github.com/safing/portmaster/spn/captain"
"github.com/safing/portmaster/spn"
"github.com/safing/portmaster/spn/conf"
)
@@ -22,45 +27,131 @@ func init() {
flag.BoolVar(&updates.RebootOnRestart, "reboot-on-restart", false, "reboot server on auto-upgrade")
}
var sigUSR1 = syscall.Signal(0xa)
func main() {
info.Set("SPN Hub", "0.7.7", "GPLv3")
flag.Parse()
// Set name and license.
info.Set("SPN Hub", "", "GPLv3")
// Configure metrics.
_ = metrics.SetNamespace("hub")
// Configure updating.
// Configure user agent and updates.
updates.UserAgent = fmt.Sprintf("SPN Hub (%s %s)", runtime.GOOS, runtime.GOARCH)
helper.IntelOnly()
// Configure SPN mode.
// Set SPN public hub mode.
conf.EnablePublicHub(true)
conf.EnableClient(false)
// Disable module management, as we want to start all modules.
modules.DisableModuleManagement()
// Set default log level.
log.SetLogLevel(log.WarningLevel)
_ = log.Start()
// Configure microtask threshold.
// Scale with CPU/GOMAXPROCS count, but keep a baseline and minimum:
// CPUs -> MicroTasks
// 0 -> 8 (increased to minimum)
// 1 -> 8 (increased to minimum)
// 2 -> 8
// 3 -> 10
// 4 -> 12
// 8 -> 20
// 16 -> 36
//
// Start with number of GOMAXPROCS.
microTasksThreshold := runtime.GOMAXPROCS(0) * 2
// Use at least 4 microtasks based on GOMAXPROCS.
if microTasksThreshold < 4 {
microTasksThreshold = 4
// Create instance.
var execCmdLine bool
instance, err := spn.New()
switch {
case err == nil:
// Continue
case errors.Is(err, mgr.ErrExecuteCmdLineOp):
execCmdLine = true
default:
fmt.Printf("error creating an instance: %s\n", err)
os.Exit(2)
}
// Add a 4 microtask baseline.
microTasksThreshold += 4
// Set threshold.
modules.SetMaxConcurrentMicroTasks(microTasksThreshold)
// Start.
os.Exit(run.Run())
// Execute command line operation, if requested or available.
switch {
case !execCmdLine:
// Run service.
case instance.CommandLineOperation == nil:
fmt.Println("command line operation execution requested, but not set")
os.Exit(3)
default:
// Run the function and exit.
err = instance.CommandLineOperation()
if err != nil {
fmt.Fprintf(os.Stderr, "command line operation failed: %s\n", err)
os.Exit(3)
}
os.Exit(0)
}
// Start
go func() {
err = instance.Start()
if err != nil {
fmt.Printf("instance start failed: %s\n", err)
os.Exit(1)
}
}()
// Wait for signal.
signalCh := make(chan os.Signal, 1)
signal.Notify(
signalCh,
os.Interrupt,
syscall.SIGHUP,
syscall.SIGINT,
syscall.SIGTERM,
syscall.SIGQUIT,
sigUSR1,
)
select {
case sig := <-signalCh:
// Only print and continue to wait if SIGUSR1
if sig == sigUSR1 {
printStackTo(os.Stderr, "PRINTING STACK ON REQUEST")
} else {
fmt.Println(" <INTERRUPT>") // CLI output.
slog.Warn("program was interrupted, stopping")
}
case <-instance.Stopped():
log.Shutdown()
os.Exit(instance.ExitCode())
}
// Catch signals during shutdown.
// Rapid unplanned disassembly after 5 interrupts.
go func() {
forceCnt := 5
for {
<-signalCh
forceCnt--
if forceCnt > 0 {
fmt.Printf(" <INTERRUPT> again, but already shutting down - %d more to force\n", forceCnt)
} else {
printStackTo(os.Stderr, "PRINTING STACK ON FORCED EXIT")
os.Exit(1)
}
}
}()
// Rapid unplanned disassembly after 3 minutes.
go func() {
time.Sleep(3 * time.Minute)
printStackTo(os.Stderr, "PRINTING STACK - TAKING TOO LONG FOR SHUTDOWN")
os.Exit(1)
}()
// Stop instance.
if err := instance.Stop(); err != nil {
slog.Error("failed to stop", "err", err)
}
log.Shutdown()
os.Exit(instance.ExitCode())
}
func printStackTo(writer io.Writer, msg string) {
_, err := fmt.Fprintf(writer, "===== %s =====\n", msg)
if err == nil {
err = pprof.Lookup("goroutine").WriteTo(writer, 1)
}
if err != nil {
slog.Error("failed to write stack trace", "err", err)
}
}

View File

@@ -8,7 +8,7 @@ import (
"strings"
"time"
"github.com/safing/portbase/log"
"github.com/safing/portmaster/base/log"
)
const (

View File

@@ -17,13 +17,12 @@ import (
"github.com/tevino/abool"
"github.com/safing/portbase/api/client"
"github.com/safing/portbase/dataroot"
"github.com/safing/portbase/info"
"github.com/safing/portbase/log"
"github.com/safing/portbase/modules"
"github.com/safing/portbase/updater"
"github.com/safing/portbase/utils"
"github.com/safing/portmaster/base/api/client"
"github.com/safing/portmaster/base/dataroot"
"github.com/safing/portmaster/base/info"
"github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/base/updater"
"github.com/safing/portmaster/base/utils"
"github.com/safing/portmaster/service/updates/helper"
)
@@ -77,10 +76,10 @@ func main() {
}
// print help
if modules.HelpFlag {
flag.Usage()
os.Exit(0)
}
// if modules.HelpFlag {
// flag.Usage()
// os.Exit(0)
// }
if showVersion {
fmt.Println(info.FullVersion())

View File

@@ -3,7 +3,7 @@ package main
import (
"fmt"
pbnotify "github.com/safing/portbase/notifications"
pbnotify "github.com/safing/portmaster/base/notifications"
)
// Notification represents a notification that is to be delivered to the user.

View File

@@ -6,10 +6,10 @@ import (
"sync"
"time"
"github.com/safing/portbase/api/client"
"github.com/safing/portbase/formats/dsd"
"github.com/safing/portbase/log"
pbnotify "github.com/safing/portbase/notifications"
"github.com/safing/portmaster/base/api/client"
"github.com/safing/portmaster/base/log"
pbnotify "github.com/safing/portmaster/base/notifications"
"github.com/safing/structures/dsd"
)
const (

View File

@@ -7,7 +7,7 @@ import (
notify "github.com/dhaavi/go-notify"
"github.com/safing/portbase/log"
"github.com/safing/portmaster/base/log"
)
type NotificationID uint32

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"sync"
"github.com/safing/portbase/log"
"github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/cmds/notifier/wintoast"
"github.com/safing/portmaster/service/updates/helper"
)

View File

@@ -1,8 +1,8 @@
package main
import (
"github.com/safing/portbase/api/client"
"github.com/safing/portbase/log"
"github.com/safing/portmaster/base/api/client"
"github.com/safing/portmaster/base/log"
)
func startShutdownEventListener() {

View File

@@ -6,9 +6,9 @@ import (
"github.com/tevino/abool"
"github.com/safing/portbase/api/client"
"github.com/safing/portbase/formats/dsd"
"github.com/safing/portbase/log"
"github.com/safing/portmaster/base/api/client"
"github.com/safing/portmaster/base/log"
"github.com/safing/structures/dsd"
)
const (

View File

@@ -3,9 +3,9 @@ package main
import (
"sync"
"github.com/safing/portbase/api/client"
"github.com/safing/portbase/formats/dsd"
"github.com/safing/portbase/log"
"github.com/safing/portmaster/base/api/client"
"github.com/safing/portmaster/base/log"
"github.com/safing/structures/dsd"
)
const (

View File

@@ -11,8 +11,8 @@ import (
"fyne.io/systray"
"github.com/safing/portbase/log"
icons "github.com/safing/portmaster/assets"
"github.com/safing/portmaster/base/log"
)
const (

View File

@@ -9,18 +9,42 @@ import (
"fmt"
"net/http"
"strings"
"sync/atomic"
"text/template"
"time"
"github.com/safing/portbase/apprise"
"github.com/safing/portbase/log"
"github.com/safing/portbase/modules"
"github.com/safing/portmaster/base/apprise"
"github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/service/intel/geoip"
"github.com/safing/portmaster/service/mgr"
)
// Apprise is the apprise notification module.
type Apprise struct {
mgr *mgr.Manager
instance instance
}
// Manager returns the module manager.
func (a *Apprise) Manager() *mgr.Manager {
return a.mgr
}
// Start starts the module.
func (a *Apprise) Start() error {
return startApprise()
}
// Stop stops the module.
func (a *Apprise) Stop() error {
return nil
}
var (
appriseModule *modules.Module
appriseNotifier *apprise.Notifier
appriseModule *Apprise
appriseShimLoaded atomic.Bool
appriseNotifier *apprise.Notifier
appriseURL string
appriseTag string
@@ -30,7 +54,7 @@ var (
)
func init() {
appriseModule = modules.Register("apprise", nil, startApprise, nil)
// appriseModule = modules.Register("apprise", nil, startApprise, nil)
flag.StringVar(&appriseURL, "apprise-url", "", "set the apprise URL to enable notifications via apprise")
flag.StringVar(&appriseTag, "apprise-tag", "", "set the apprise tag(s) according to their docs")
@@ -78,7 +102,7 @@ func startApprise() error {
}
if appriseGreet {
err := appriseNotifier.Send(appriseModule.Ctx, &apprise.Message{
err := appriseNotifier.Send(appriseModule.mgr.Ctx(), &apprise.Message{
Title: "👋 Observation Hub Reporting In",
Body: "I am the Observation Hub. I am connected to the SPN and watch out for it. I will report notable changes to the network here.",
})
@@ -101,7 +125,7 @@ func reportToApprise(change *observedChange) (errs error) {
handleTag:
for _, tag := range strings.Split(appriseNotifier.DefaultTag, ",") {
// Check if we are shutting down.
if appriseModule.IsStopping() {
if appriseModule.mgr.IsDone() {
return nil
}
@@ -127,9 +151,9 @@ handleTag:
// Send notification to apprise.
var err error
for i := 0; i < 3; i++ {
for range 3 {
// Try three times.
err = appriseNotifier.Send(appriseModule.Ctx, &apprise.Message{
err = appriseNotifier.Send(appriseModule.mgr.Ctx(), &apprise.Message{
Body: buf.String(),
Tag: tag,
})
@@ -255,3 +279,18 @@ func getCountryInfo(code string) geoip.CountryInfo {
// panic(err)
// }
// }
// NewApprise returns a new Apprise module.
func NewApprise(instance instance) (*Observer, error) {
if !appriseShimLoaded.CompareAndSwap(false, true) {
return nil, errors.New("only one instance allowed")
}
m := mgr.New("apprise")
appriseModule = &Apprise{
mgr: m,
instance: instance,
}
return observerModule, nil
}

View File

@@ -56,5 +56,5 @@ echo "This information is useful for debugging and license compliance."
echo "Run the compiled binary with the -version flag to see the information included."
# build
BUILD_PATH="github.com/safing/portbase/info"
BUILD_PATH="github.com/safing/portmaster/base/info"
go build $DEV -ldflags "-X ${BUILD_PATH}.commit=${BUILD_COMMIT} -X ${BUILD_PATH}.buildOptions=${BUILD_BUILDOPTIONS} -X ${BUILD_PATH}.buildUser=${BUILD_USER} -X ${BUILD_PATH}.buildHost=${BUILD_HOST} -X ${BUILD_PATH}.buildDate=${BUILD_DATE} -X ${BUILD_PATH}.buildSource=${BUILD_SOURCE}" $*

View File

@@ -1,44 +1,175 @@
package main
import (
"errors"
"flag"
"fmt"
"io"
"log/slog"
"os"
"os/signal"
"runtime"
"runtime/pprof"
"syscall"
"time"
"github.com/safing/portbase/api"
"github.com/safing/portbase/info"
"github.com/safing/portbase/metrics"
"github.com/safing/portbase/modules"
"github.com/safing/portbase/run"
"github.com/safing/portmaster/base/api"
"github.com/safing/portmaster/base/info"
"github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/base/metrics"
"github.com/safing/portmaster/service/mgr"
"github.com/safing/portmaster/service/updates"
"github.com/safing/portmaster/service/updates/helper"
"github.com/safing/portmaster/spn"
"github.com/safing/portmaster/spn/captain"
"github.com/safing/portmaster/spn/conf"
"github.com/safing/portmaster/spn/sluice"
)
var sigUSR1 = syscall.Signal(0xa)
func main() {
info.Set("SPN Observation Hub", "0.7.1", "GPLv3")
flag.Parse()
info.Set("SPN Observation Hub", "", "GPLv3")
// Configure metrics.
_ = metrics.SetNamespace("observer")
// Configure user agent.
// Configure user agent and updates.
updates.UserAgent = fmt.Sprintf("SPN Observation Hub (%s %s)", runtime.GOOS, runtime.GOARCH)
helper.IntelOnly()
// Configure SPN mode.
conf.EnableClient(true)
conf.EnablePublicHub(false)
captain.DisableAccount = true
// Disable unneeded listeners.
sluice.EnableListener = false
api.EnableServer = false
// Disable module management, as we want to start all modules.
modules.DisableModuleManagement()
// Set default log level.
log.SetLogLevel(log.WarningLevel)
_ = log.Start()
// Start.
os.Exit(run.Run())
// Create instance.
var execCmdLine bool
instance, err := spn.New()
switch {
case err == nil:
// Continue
case errors.Is(err, mgr.ErrExecuteCmdLineOp):
execCmdLine = true
default:
fmt.Printf("error creating an instance: %s\n", err)
os.Exit(2)
}
// Add additional modules.
observer, err := New(instance)
if err != nil {
fmt.Printf("error creating an instance: create observer module: %s\n", err)
os.Exit(2)
}
instance.AddModule(observer)
_, err = NewApprise(instance)
if err != nil {
fmt.Printf("error creating an instance: create apprise module: %s\n", err)
os.Exit(2)
}
instance.AddModule(observer)
// Execute command line operation, if requested or available.
switch {
case !execCmdLine:
// Run service.
case instance.CommandLineOperation == nil:
fmt.Println("command line operation execution requested, but not set")
os.Exit(3)
default:
// Run the function and exit.
err = instance.CommandLineOperation()
if err != nil {
fmt.Fprintf(os.Stderr, "command line operation failed: %s\n", err)
os.Exit(3)
}
os.Exit(0)
}
// Start
go func() {
err = instance.Start()
if err != nil {
fmt.Printf("instance start failed: %s\n", err)
os.Exit(1)
}
}()
// Wait for signal.
signalCh := make(chan os.Signal, 1)
signal.Notify(
signalCh,
os.Interrupt,
syscall.SIGHUP,
syscall.SIGINT,
syscall.SIGTERM,
syscall.SIGQUIT,
sigUSR1,
)
select {
case sig := <-signalCh:
// Only print and continue to wait if SIGUSR1
if sig == sigUSR1 {
printStackTo(os.Stderr, "PRINTING STACK ON REQUEST")
} else {
fmt.Println(" <INTERRUPT>") // CLI output.
slog.Warn("program was interrupted, stopping")
}
case <-instance.Stopped():
log.Shutdown()
os.Exit(instance.ExitCode())
}
// Catch signals during shutdown.
// Rapid unplanned disassembly after 5 interrupts.
go func() {
forceCnt := 5
for {
<-signalCh
forceCnt--
if forceCnt > 0 {
fmt.Printf(" <INTERRUPT> again, but already shutting down - %d more to force\n", forceCnt)
} else {
printStackTo(os.Stderr, "PRINTING STACK ON FORCED EXIT")
os.Exit(1)
}
}
}()
// Rapid unplanned disassembly after 3 minutes.
go func() {
time.Sleep(3 * time.Minute)
printStackTo(os.Stderr, "PRINTING STACK - TAKING TOO LONG FOR SHUTDOWN")
os.Exit(1)
}()
// Stop instance.
if err := instance.Stop(); err != nil {
slog.Error("failed to stop", "err", err)
}
log.Shutdown()
os.Exit(instance.ExitCode())
}
func printStackTo(writer io.Writer, msg string) {
_, err := fmt.Fprintf(writer, "===== %s =====\n", msg)
if err == nil {
err = pprof.Lookup("goroutine").WriteTo(writer, 1)
}
if err != nil {
slog.Error("failed to write stack trace", "err", err)
}
}

View File

@@ -1,27 +1,49 @@
package main
import (
"context"
"errors"
"flag"
"fmt"
"path"
"strings"
"sync/atomic"
"time"
diff "github.com/r3labs/diff/v3"
"golang.org/x/exp/slices"
"github.com/safing/portbase/database"
"github.com/safing/portbase/database/query"
"github.com/safing/portbase/log"
"github.com/safing/portbase/modules"
"github.com/safing/portmaster/base/database"
"github.com/safing/portmaster/base/database/query"
"github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/service/mgr"
"github.com/safing/portmaster/spn/captain"
"github.com/safing/portmaster/spn/navigator"
)
// Observer is the network observer module.
type Observer struct {
mgr *mgr.Manager
instance instance
}
// Manager returns the module manager.
func (o *Observer) Manager() *mgr.Manager {
return o.mgr
}
// Start starts the module.
func (o *Observer) Start() error {
return startObserver()
}
// Stop stops the module.
func (o *Observer) Stop() error {
return nil
}
var (
observerModule *modules.Module
observerModule *Observer
shimLoaded atomic.Bool
db = database.NewInterface(&database.Options{
Local: true,
@@ -33,12 +55,11 @@ var (
errNoChanges = errors.New("no changes")
reportingDelayFlag string
reportingDelay = 10 * time.Minute
reportingDelay = 5 * time.Minute
reportingMaxDelay = reportingDelay * 3
)
func init() {
observerModule = modules.Register("observer", prepObserver, startObserver, nil, "captain", "apprise")
flag.BoolVar(&reportAllChanges, "report-all-changes", false, "report all changes, no just interesting ones")
flag.StringVar(&reportingDelayFlag, "reporting-delay", "10m", "delay reports to summarize changes")
}
@@ -51,12 +72,13 @@ func prepObserver() error {
}
reportingDelay = duration
}
reportingMaxDelay = reportingDelay * 3
return nil
}
func startObserver() error {
observerModule.StartServiceWorker("observer", 0, observerWorker)
observerModule.mgr.Go("observer", observerWorker)
return nil
}
@@ -65,8 +87,9 @@ type observedPin struct {
previous *navigator.PinExport
latest *navigator.PinExport
lastUpdate time.Time
lastUpdateReported bool
firstUpdate time.Time
lastUpdate time.Time
updateReported bool
}
type observedChange struct {
@@ -79,7 +102,7 @@ type observedChange struct {
SPNStatus *captain.SPNStatus
}
func observerWorker(ctx context.Context) error {
func observerWorker(ctx *mgr.WorkerCtx) error {
log.Info("observer: starting")
defer log.Info("observer: stopped")
@@ -133,19 +156,20 @@ func observerWorker(ctx context.Context) error {
// Put all current pins in a map.
observedPins := make(map[string]*observedPin)
query:
initialQuery:
for {
select {
case r := <-q.Next:
// Check if we are done.
if r == nil {
break query
break initialQuery
}
// Add all pins to seen pins.
if pin, ok := r.(*navigator.PinExport); ok {
observedPins[pin.ID] = &observedPin{
previous: pin,
latest: pin,
previous: pin,
latest: pin,
updateReported: true,
}
} else {
log.Warningf("observer: received invalid pin export: %s", r)
@@ -200,13 +224,18 @@ query:
if ok {
// Update previously observed Hub.
existingObservedPin.latest = pin
if existingObservedPin.updateReported {
existingObservedPin.firstUpdate = time.Now()
}
existingObservedPin.lastUpdate = time.Now()
existingObservedPin.lastUpdateReported = false
existingObservedPin.updateReported = false
} else {
// Add new Hub.
observedPins[pin.ID] = &observedPin{
latest: pin,
lastUpdate: time.Now(),
latest: pin,
firstUpdate: time.Now(),
lastUpdate: time.Now(),
updateReported: false,
}
}
} else {
@@ -226,15 +255,19 @@ query:
}
switch {
case observedPin.lastUpdateReported:
case observedPin.updateReported:
// Change already reported.
case time.Since(observedPin.lastUpdate) < reportingDelay:
case time.Since(observedPin.lastUpdate) < reportingDelay &&
time.Since(observedPin.firstUpdate) < reportingMaxDelay:
// Only report changes if older than the configured delay.
// Up to a maximum delay.
default:
// Format and report.
title, changes, err := formatPinChanges(observedPin.previous, observedPin.latest)
if err != nil {
if !errors.Is(err, errNoChanges) {
if errors.Is(err, errNoChanges) {
log.Debugf("observer: no reportable changes found for %s", observedPin.latest.HumanName())
} else {
log.Warningf("observer: failed to format pin changes: %s", err)
}
} else {
@@ -250,7 +283,7 @@ query:
// Update observed pin.
observedPin.previous = observedPin.latest
observedPin.lastUpdateReported = true
observedPin.updateReported = true
}
}
}
@@ -405,3 +438,24 @@ func makeHubName(name, id string) string {
return fmt.Sprintf("%s (%s)", name, shortenedID)
}
}
// New returns a new Observer module.
func New(instance instance) (*Observer, error) {
if !shimLoaded.CompareAndSwap(false, true) {
return nil, errors.New("only one instance allowed")
}
m := mgr.New("observer")
observerModule = &Observer{
mgr: m,
instance: instance,
}
if err := prepObserver(); err != nil {
return nil, err
}
return observerModule, nil
}
type instance interface{}

View File

@@ -10,5 +10,5 @@ BUILD_TIME=$(date -u "+%Y-%m-%dT%H:%M:%SZ" || echo "unknown")
# Build
export CGO_ENABLED=0
BUILD_PATH="github.com/safing/portbase/info"
go build -ldflags "-X github.com/safing/portbase/info.version=${VERSION} -X github.com/safing/portbase/info.buildSource=${SOURCE} -X github.com/safing/portbase/info.buildTime=${BUILD_TIME}" "$@"
BUILD_PATH="github.com/safing/portmaster/base/info"
go build -ldflags "-X github.com/safing/portmaster/base/info.version=${VERSION} -X github.com/safing/portmaster/base/info.buildSource=${SOURCE} -X github.com/safing/portmaster/base/info.buildTime=${BUILD_TIME}" "$@"

View File

@@ -2,33 +2,35 @@
package main
import (
"errors"
"flag"
"fmt"
"io"
"log/slog"
"os"
"os/signal"
"runtime"
"runtime/pprof"
"syscall"
"time"
"github.com/safing/portbase/info"
"github.com/safing/portbase/log"
"github.com/safing/portbase/metrics"
"github.com/safing/portbase/run"
"github.com/safing/portmaster/base/info"
"github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/base/metrics"
"github.com/safing/portmaster/service"
"github.com/safing/portmaster/service/mgr"
"github.com/safing/portmaster/service/updates"
"github.com/safing/portmaster/spn/conf"
// Include packages here.
_ "github.com/safing/portbase/modules/subsystems"
_ "github.com/safing/portmaster/service/core"
_ "github.com/safing/portmaster/service/firewall"
_ "github.com/safing/portmaster/service/nameserver"
_ "github.com/safing/portmaster/service/ui"
_ "github.com/safing/portmaster/spn/captain"
)
var sigUSR1 = syscall.Signal(0xa)
func main() {
flag.Parse()
// set information
info.Set("Portmaster", "", "GPLv3")
// Set default log level.
log.SetLogLevel(log.WarningLevel)
// Configure metrics.
_ = metrics.SetNamespace("portmaster")
@@ -37,7 +39,115 @@ func main() {
// enable SPN client mode
conf.EnableClient(true)
conf.EnableIntegration(true)
// start
os.Exit(run.Run())
// Create instance.
var execCmdLine bool
instance, err := service.New(&service.ServiceConfig{})
switch {
case err == nil:
// Continue
case errors.Is(err, mgr.ErrExecuteCmdLineOp):
execCmdLine = true
default:
fmt.Printf("error creating an instance: %s\n", err)
os.Exit(2)
}
// Execute command line operation, if requested or available.
switch {
case !execCmdLine:
// Run service.
case instance.CommandLineOperation == nil:
fmt.Println("command line operation execution requested, but not set")
os.Exit(3)
default:
// Run the function and exit.
err = instance.CommandLineOperation()
if err != nil {
fmt.Fprintf(os.Stderr, "command line operation failed: %s\n", err)
os.Exit(3)
}
os.Exit(0)
}
// Set default log level.
log.SetLogLevel(log.WarningLevel)
_ = log.Start()
// Start
go func() {
err = instance.Start()
if err != nil {
fmt.Printf("instance start failed: %s\n", err)
os.Exit(1)
}
}()
// Wait for signal.
signalCh := make(chan os.Signal, 1)
signal.Notify(
signalCh,
os.Interrupt,
syscall.SIGHUP,
syscall.SIGINT,
syscall.SIGTERM,
syscall.SIGQUIT,
sigUSR1,
)
select {
case sig := <-signalCh:
// Only print and continue to wait if SIGUSR1
if sig == sigUSR1 {
printStackTo(os.Stderr, "PRINTING STACK ON REQUEST")
} else {
fmt.Println(" <INTERRUPT>") // CLI output.
slog.Warn("program was interrupted, stopping")
}
case <-instance.Stopped():
log.Shutdown()
os.Exit(instance.ExitCode())
}
// Catch signals during shutdown.
// Rapid unplanned disassembly after 5 interrupts.
go func() {
forceCnt := 5
for {
<-signalCh
forceCnt--
if forceCnt > 0 {
fmt.Printf(" <INTERRUPT> again, but already shutting down - %d more to force\n", forceCnt)
} else {
printStackTo(os.Stderr, "PRINTING STACK ON FORCED EXIT")
os.Exit(1)
}
}
}()
// Rapid unplanned disassembly after 3 minutes.
go func() {
time.Sleep(3 * time.Minute)
printStackTo(os.Stderr, "PRINTING STACK - TAKING TOO LONG FOR SHUTDOWN")
os.Exit(1)
}()
// Stop instance.
if err := instance.Stop(); err != nil {
slog.Error("failed to stop", "err", err)
}
log.Shutdown()
os.Exit(instance.ExitCode())
}
func printStackTo(writer io.Writer, msg string) {
_, err := fmt.Fprintf(writer, "===== %s =====\n", msg)
if err == nil {
err = pprof.Lookup("goroutine").WriteTo(writer, 1)
}
if err != nil {
slog.Error("failed to write stack trace", "err", err)
}
}

View File

@@ -73,5 +73,5 @@ echo "This information is useful for debugging and license compliance."
echo "Run the compiled binary with the -version flag to see the information included."
# build
BUILD_PATH="github.com/safing/portbase/info"
BUILD_PATH="github.com/safing/portmaster/base/info"
go build -ldflags "$EXTRA_LD_FLAGS -X ${BUILD_PATH}.commit=${BUILD_COMMIT} -X ${BUILD_PATH}.buildOptions=${BUILD_BUILDOPTIONS} -X ${BUILD_PATH}.buildUser=${BUILD_USER} -X ${BUILD_PATH}.buildHost=${BUILD_HOST} -X ${BUILD_PATH}.buildDate=${BUILD_DATE} -X ${BUILD_PATH}.buildSource=${BUILD_SOURCE}" "$@"

View File

@@ -10,10 +10,10 @@ import (
"github.com/spf13/cobra"
"github.com/safing/portbase/container"
"github.com/safing/portbase/database/record"
"github.com/safing/portbase/formats/dsd"
"github.com/safing/portbase/info"
"github.com/safing/portmaster/base/database/record"
"github.com/safing/portmaster/base/info"
"github.com/safing/structures/container"
"github.com/safing/structures/dsd"
)
func initializeLogFile(logFilePath string, identifier string, version string) *os.File {

View File

@@ -15,11 +15,11 @@ import (
"github.com/spf13/cobra"
"github.com/safing/portbase/dataroot"
"github.com/safing/portbase/info"
portlog "github.com/safing/portbase/log"
"github.com/safing/portbase/updater"
"github.com/safing/portbase/utils"
"github.com/safing/portmaster/base/dataroot"
"github.com/safing/portmaster/base/info"
portlog "github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/base/updater"
"github.com/safing/portmaster/base/utils"
"github.com/safing/portmaster/service/updates/helper"
)

View File

@@ -8,8 +8,8 @@ import (
"github.com/spf13/cobra"
portlog "github.com/safing/portbase/log"
"github.com/safing/portbase/updater"
portlog "github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/base/updater"
"github.com/safing/portmaster/service/updates/helper"
)

View File

@@ -13,8 +13,8 @@ import (
"github.com/safing/jess"
"github.com/safing/jess/filesig"
portlog "github.com/safing/portbase/log"
"github.com/safing/portbase/updater"
portlog "github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/base/updater"
"github.com/safing/portmaster/service/updates/helper"
)

View File

@@ -10,7 +10,7 @@ import (
"github.com/spf13/cobra"
"github.com/safing/portbase/info"
"github.com/safing/portmaster/base/info"
)
var (

View File

@@ -1,8 +1,8 @@
package main
import (
"github.com/safing/portbase/database"
_ "github.com/safing/portbase/database/storage/hashmap"
"github.com/safing/portmaster/base/database"
_ "github.com/safing/portmaster/base/database/storage/hashmap"
)
func setupDatabases(path string) error {

View File

@@ -9,7 +9,7 @@ import (
"github.com/miekg/dns"
"github.com/safing/portbase/log"
"github.com/safing/portmaster/base/log"
)
const dnsResolver = "1.1.1.1:53"

View File

@@ -7,8 +7,8 @@ import (
"github.com/spf13/cobra"
"github.com/safing/portbase/updater"
"github.com/safing/portbase/utils"
"github.com/safing/portmaster/base/updater"
"github.com/safing/portmaster/base/utils"
)
var (

View File

@@ -5,7 +5,7 @@ import (
"github.com/spf13/cobra"
"github.com/safing/portbase/log"
"github.com/safing/portmaster/base/log"
)
func init() {

View File

@@ -10,7 +10,7 @@ import (
"github.com/spf13/cobra"
"github.com/safing/portbase/updater"
"github.com/safing/portmaster/base/updater"
)
var (

View File

@@ -12,7 +12,7 @@ import (
"path/filepath"
"syscall"
"github.com/safing/portbase/log"
"github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/service/firewall/interception/windowskext"
"github.com/safing/portmaster/service/network/packet"
)