Skip to content

Commit 61fe269

Browse files
committedMar 3, 2025·
Pull request 2352: AGDNS-2690-signal-handler
Merge in DNS/adguard-home from AGDNS-2690-signal-handler to master Squashed commit of the following: commit b682214 Merge: f597ea1 8b2ab8e Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Mon Mar 3 18:10:27 2025 +0300 Merge branch 'master' into AGDNS-2690-signal-handler commit f597ea1 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Thu Feb 27 20:45:55 2025 +0300 all: fix logger commit 582e315 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Thu Feb 27 18:40:57 2025 +0300 home: imp code commit 1e0f48d Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Thu Feb 27 15:26:19 2025 +0300 home: imp docs commit aa43bbc Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Mon Feb 24 17:24:33 2025 +0300 home: signal handler
1 parent 8b2ab8e commit 61fe269

File tree

7 files changed

+167
-39
lines changed

7 files changed

+167
-39
lines changed
 

‎go.mod

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.23.6
44

55
require (
66
github.com/AdguardTeam/dnsproxy v0.75.0
7-
github.com/AdguardTeam/golibs v0.32.1
7+
github.com/AdguardTeam/golibs v0.32.5
88
github.com/AdguardTeam/urlfilter v0.20.0
99
github.com/NYTimes/gziphandler v1.1.1
1010
github.com/ameshkov/dnscrypt/v2 v2.3.0
@@ -33,9 +33,9 @@ require (
3333
github.com/stretchr/testify v1.10.0
3434
github.com/ti-mo/netfilter v0.5.2
3535
go.etcd.io/bbolt v1.4.0
36-
golang.org/x/crypto v0.32.0
37-
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3
38-
golang.org/x/net v0.34.0
36+
golang.org/x/crypto v0.33.0
37+
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa
38+
golang.org/x/net v0.35.0
3939
golang.org/x/sys v0.30.0
4040
gopkg.in/natefinch/lumberjack.v2 v2.2.1
4141
gopkg.in/yaml.v3 v3.0.1
@@ -63,6 +63,6 @@ require (
6363
golang.org/x/mod v0.23.0 // indirect
6464
golang.org/x/sync v0.11.0 // indirect
6565
golang.org/x/text v0.22.0 // indirect
66-
golang.org/x/tools v0.29.0 // indirect
66+
golang.org/x/tools v0.30.0 // indirect
6767
gonum.org/v1/gonum v0.15.1 // indirect
6868
)

‎go.sum

+12-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
github.com/AdguardTeam/dnsproxy v0.75.0 h1:v8/Oq/xPYzNoALR7SEUZEIbKmjnPcXLVhJLFVbrozEc=
22
github.com/AdguardTeam/dnsproxy v0.75.0/go.mod h1:O2qoXwF4BUBFui7OMUiWSYwapEDcYxKWeur4+jfy9nM=
3-
github.com/AdguardTeam/golibs v0.32.1 h1:Ajf6Q0k+A9zjFbj8HOzNAbHImrV4JtbT0vwy02D6VeI=
4-
github.com/AdguardTeam/golibs v0.32.1/go.mod h1:dXRLSsnJQDxOfQVl0ochy1bfk4NgnJQGQdR1YPJdwcw=
3+
github.com/AdguardTeam/golibs v0.32.5 h1:4Rkv2xBnyJe6l/EM2MFgoY1S4pweYwDgLTYg2MDArEA=
4+
github.com/AdguardTeam/golibs v0.32.5/go.mod h1:agsvz8Iyv0uV9NU56hpCoFLAtSPkiBf9nPVhDvdUIb0=
55
github.com/AdguardTeam/urlfilter v0.20.0 h1:X32qiuVCVd8WDYCEsbdZKfXMzwdVqrdulamtUi4rmzs=
66
github.com/AdguardTeam/urlfilter v0.20.0/go.mod h1:gjrywLTxfJh6JOkwi9SU+frhP7kVVEZ5exFGkR99qpk=
77
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
@@ -128,10 +128,10 @@ go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
128128
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
129129
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
130130
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
131-
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
132-
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
133-
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 h1:qNgPs5exUA+G0C96DrPwNrvLSj7GT/9D+3WMWUcUg34=
134-
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
131+
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
132+
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
133+
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4=
134+
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
135135
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
136136
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
137137
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
@@ -142,8 +142,8 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn
142142
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
143143
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
144144
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
145-
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
146-
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
145+
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
146+
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
147147
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
148148
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
149149
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
@@ -169,14 +169,14 @@ golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
169169
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
170170
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
171171
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
172-
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
173-
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
172+
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
173+
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
174174
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
175175
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
176176
gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0=
177177
gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o=
178-
google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=
179-
google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
178+
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
179+
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
180180
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
181181
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
182182
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

‎internal/home/clients.go

+3
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ func (clients *clientsContainer) Init(
7575
etcHosts *aghnet.HostsContainer,
7676
arpDB arpdb.Interface,
7777
filteringConf *filtering.Config,
78+
sigHdlr *signalHandler,
7879
) (err error) {
7980
// TODO(s.chzhen): Refactor it.
8081
if clients.storage != nil {
@@ -120,6 +121,8 @@ func (clients *clientsContainer) Init(
120121
return fmt.Errorf("init client storage: %w", err)
121122
}
122123

124+
sigHdlr.addClientStorage(clients.storage)
125+
123126
return nil
124127
}
125128

‎internal/home/clients_internal_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ func newClientsContainer(t *testing.T) (c *clientsContainer) {
3131
nil,
3232
nil,
3333
&filtering.Config{},
34+
newSignalHandler(nil, nil),
3435
)
3536

3637
require.NoError(t, err)

‎internal/home/home.go

+21-21
Original file line numberDiff line numberDiff line change
@@ -113,31 +113,23 @@ func Main(clientBuildFS fs.FS) {
113113
signals := make(chan os.Signal, 1)
114114
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
115115

116-
go func() {
117-
ctx := context.Background()
118-
for {
119-
sig := <-signals
120-
log.Info("Received signal %q", sig)
121-
switch sig {
122-
case syscall.SIGHUP:
123-
globalContext.clients.storage.ReloadARP(ctx)
124-
globalContext.tls.reload()
125-
default:
126-
cleanup(ctx)
127-
cleanupAlways()
128-
close(done)
129-
}
130-
}
131-
}()
116+
ctx := context.Background()
117+
sigHdlr := newSignalHandler(signals, func(ctx context.Context) {
118+
cleanup(ctx)
119+
cleanupAlways()
120+
close(done)
121+
})
122+
123+
go sigHdlr.handle(ctx)
132124

133125
if opts.serviceControlAction != "" {
134-
handleServiceControlAction(opts, clientBuildFS, signals, done)
126+
handleServiceControlAction(opts, clientBuildFS, signals, done, sigHdlr)
135127

136128
return
137129
}
138130

139131
// run the protection
140-
run(opts, clientBuildFS, done)
132+
run(opts, clientBuildFS, done, sigHdlr)
141133
}
142134

143135
// setupContext initializes [globalContext] fields. It also reads and upgrades
@@ -278,7 +270,11 @@ func setupOpts(opts options) (err error) {
278270
}
279271

280272
// initContextClients initializes Context clients and related fields.
281-
func initContextClients(ctx context.Context, logger *slog.Logger) (err error) {
273+
func initContextClients(
274+
ctx context.Context,
275+
logger *slog.Logger,
276+
sigHdlr *signalHandler,
277+
) (err error) {
282278
err = setupDNSFilteringConf(ctx, logger, config.Filtering)
283279
if err != nil {
284280
// Don't wrap the error, because it's informative enough as is.
@@ -313,6 +309,7 @@ func initContextClients(ctx context.Context, logger *slog.Logger) (err error) {
313309
globalContext.etcHosts,
314310
arpDB,
315311
config.Filtering,
312+
sigHdlr,
316313
)
317314
}
318315

@@ -583,7 +580,7 @@ func fatalOnError(err error) {
583580
// run configures and starts AdGuard Home.
584581
//
585582
// TODO(e.burkov): Make opts a pointer.
586-
func run(opts options, clientBuildFS fs.FS, done chan struct{}) {
583+
func run(opts options, clientBuildFS fs.FS, done chan struct{}, sigHdlr *signalHandler) {
587584
// Configure working dir.
588585
err := initWorkingDir(opts)
589586
fatalOnError(err)
@@ -599,6 +596,7 @@ func run(opts options, clientBuildFS fs.FS, done chan struct{}) {
599596

600597
// TODO(a.garipov): Use slog everywhere.
601598
slogLogger := newSlogLogger(ls)
599+
sigHdlr.swapLogger(slogLogger)
602600

603601
// Print the first message after logger is configured.
604602
log.Info(version.Full())
@@ -621,7 +619,7 @@ func run(opts options, clientBuildFS fs.FS, done chan struct{}) {
621619
// TODO(s.chzhen): Use it for the entire initialization process.
622620
ctx := context.Background()
623621

624-
err = initContextClients(ctx, slogLogger)
622+
err = initContextClients(ctx, slogLogger, sigHdlr)
625623
fatalOnError(err)
626624

627625
err = setupOpts(opts)
@@ -664,6 +662,8 @@ func run(opts options, clientBuildFS fs.FS, done chan struct{}) {
664662
onConfigModified()
665663
}
666664

665+
sigHdlr.addTLSManager(globalContext.tls)
666+
667667
globalContext.web, err = initWeb(ctx, opts, clientBuildFS, upd, slogLogger, customURL)
668668
fatalOnError(err)
669669

‎internal/home/service.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ type program struct {
3636
signals chan os.Signal
3737
done chan struct{}
3838
opts options
39+
sigHdlr *signalHandler
3940
}
4041

4142
// type check
@@ -47,7 +48,7 @@ func (p *program) Start(_ service.Service) (err error) {
4748
args := p.opts
4849
args.runningAsService = true
4950

50-
go run(args, p.clientBuildFS, p.done)
51+
go run(args, p.clientBuildFS, p.done, p.sigHdlr)
5152

5253
return nil
5354
}
@@ -204,6 +205,7 @@ func handleServiceControlAction(
204205
clientBuildFS fs.FS,
205206
signals chan os.Signal,
206207
done chan struct{},
208+
sigHdlr *signalHandler,
207209
) {
208210
// Call chooseSystem explicitly to introduce OpenBSD support for service
209211
// package. It's a noop for other GOOS values.
@@ -244,6 +246,7 @@ func handleServiceControlAction(
244246
signals: signals,
245247
done: done,
246248
opts: runOpts,
249+
sigHdlr: sigHdlr,
247250
}, svcConfig)
248251
if err != nil {
249252
log.Fatalf("service: initializing service: %s", err)

‎internal/home/signal.go

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package home
2+
3+
import (
4+
"context"
5+
"log/slog"
6+
"os"
7+
"sync"
8+
"sync/atomic"
9+
"syscall"
10+
11+
"github.com/AdguardTeam/AdGuardHome/internal/client"
12+
"github.com/AdguardTeam/golibs/logutil/slogutil"
13+
"github.com/AdguardTeam/golibs/osutil"
14+
)
15+
16+
// signalHandler processes incoming signals. It reloads configurations of
17+
// stored entities on SIGHUP and performs cleanup on all other signals.
18+
type signalHandler struct {
19+
// logger is used to log the operation of the signal handler. Initially,
20+
// [slog.Default] is used, but it should be swapped later using
21+
// [signalHandler.swapLogger].
22+
logger *atomic.Pointer[slog.Logger]
23+
24+
// mu protects clientStorage and tlsManager.
25+
mu *sync.Mutex
26+
27+
// clientStorage is used to reload information about runtime clients with an
28+
// ARP source.
29+
clientStorage *client.Storage
30+
31+
// tlsManager is used to reload the TLS configuration.
32+
tlsManager *tlsManager
33+
34+
// signals receives incoming signals.
35+
signals <-chan os.Signal
36+
37+
// cleanup is called to perform cleanup on all incoming signals, except
38+
// SIGHUP.
39+
cleanup func(ctx context.Context)
40+
}
41+
42+
// newSignalHandler returns a new properly initialized *signalHandler.
43+
func newSignalHandler(
44+
signals <-chan os.Signal,
45+
cleanup func(ctx context.Context),
46+
) (h *signalHandler) {
47+
h = &signalHandler{
48+
logger: &atomic.Pointer[slog.Logger]{},
49+
mu: &sync.Mutex{},
50+
signals: signals,
51+
cleanup: cleanup,
52+
}
53+
54+
h.logger.Store(slog.Default())
55+
56+
return h
57+
}
58+
59+
// swapLogger replaces the stored logger with the given logger.
60+
func (h *signalHandler) swapLogger(logger *slog.Logger) {
61+
h.logger.Swap(logger)
62+
}
63+
64+
// addClientStorage stores the client storage.
65+
func (h *signalHandler) addClientStorage(s *client.Storage) {
66+
h.mu.Lock()
67+
defer h.mu.Unlock()
68+
69+
h.clientStorage = s
70+
}
71+
72+
// addTLSManager stores the TLS manager.
73+
func (h *signalHandler) addTLSManager(m *tlsManager) {
74+
h.mu.Lock()
75+
defer h.mu.Unlock()
76+
77+
h.tlsManager = m
78+
}
79+
80+
// handle processes incoming signals. It blocks until a signal is received. It
81+
// reloads configurations of stored entities on SIGHUP, or performs cleanup on
82+
// all other signals. It is intended to be used as a goroutine.
83+
func (h *signalHandler) handle(ctx context.Context) {
84+
// NOTE: Avoid using [slogutil.RecoverAndExit] to prevent immediate
85+
// evaluation of the logger.
86+
defer func() {
87+
v := recover()
88+
if v == nil {
89+
return
90+
}
91+
92+
slogutil.PrintRecovered(ctx, h.logger.Load(), v)
93+
94+
os.Exit(osutil.ExitCodeFailure)
95+
}()
96+
97+
for {
98+
sig := <-h.signals
99+
h.logger.Load().InfoContext(ctx, "received signal", "signal", sig)
100+
switch sig {
101+
case syscall.SIGHUP:
102+
h.reloadConfig(ctx)
103+
default:
104+
h.cleanup(ctx)
105+
}
106+
}
107+
}
108+
109+
// reloadConfig refreshes configurations of stored entities.
110+
func (h *signalHandler) reloadConfig(ctx context.Context) {
111+
h.mu.Lock()
112+
defer h.mu.Unlock()
113+
114+
if h.clientStorage != nil {
115+
h.clientStorage.ReloadARP(ctx)
116+
}
117+
118+
if h.tlsManager != nil {
119+
h.tlsManager.reload()
120+
}
121+
}

0 commit comments

Comments
 (0)
Please sign in to comment.