mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2025-12-20 01:11:03 +08:00
Pull request 2506: AGDNS-3334-imp-home
Squashed commit of the following: commit ae110ad162ea7f30854750255a378f3b6484de94 Merge:965d052f93bebde610Author: f.setrakov <f.setrakov@adguard.com> Date: Sat Nov 1 13:05:43 2025 +0300 Merge branch 'master' into AGDNS-3334-imp-home commit965d052f96Author: f.setrakov <f.setrakov@adguard.com> Date: Mon Oct 27 15:58:20 2025 +0300 home: imp docs commite53565e055Merge:3bafcd30c8b8bfb21eAuthor: f.setrakov <f.setrakov@adguard.com> Date: Mon Oct 27 15:50:12 2025 +0300 Merge branch 'master' into AGDNS-3334-imp-home commit3bafcd30cdAuthor: f.setrakov <f.setrakov@adguard.com> Date: Fri Oct 24 16:53:48 2025 +0300 all: imp style commitb762eaa51cAuthor: f.setrakov <f.setrakov@adguard.com> Date: Thu Oct 23 17:08:48 2025 +0300 home: imp docs commitc54a961c2eAuthor: f.setrakov <f.setrakov@adguard.com> Date: Thu Oct 23 11:21:03 2025 +0300 home: fix tls server commit0801b367f8Author: f.setrakov <f.setrakov@adguard.com> Date: Wed Oct 22 18:33:48 2025 +0300 home: imp style commitf148198fe0Author: f.setrakov <f.setrakov@adguard.com> Date: Wed Oct 22 14:35:12 2025 +0300 home: imp allocs commitdbe768ec96Author: f.setrakov <f.setrakov@adguard.com> Date: Wed Oct 22 14:20:38 2025 +0300 home: fix docs, style commit74e88ba044Author: f.setrakov <f.setrakov@adguard.com> Date: Wed Oct 22 14:07:24 2025 +0300 home: imp docs, imp maintain commit6260af1aceAuthor: f.setrakov <f.setrakov@adguard.com> Date: Tue Oct 21 20:00:30 2025 +0300 scripts: changed gocognit value for home pkg commit4a5dfaa618Author: f.setrakov <f.setrakov@adguard.com> Date: Tue Oct 21 19:51:38 2025 +0300 all: imp style commit18f9d9a022Author: f.setrakov <f.setrakov@adguard.com> Date: Mon Oct 20 19:17:36 2025 +0300 home: imp cognit complexity
This commit is contained in:
@@ -374,32 +374,11 @@ func (mw *authMiddlewareDefault) Wrap(h http.Handler) (wrapped http.Handler) {
|
||||
}
|
||||
|
||||
path := r.URL.Path
|
||||
u, err := mw.userFromRequest(ctx, r)
|
||||
if err != nil {
|
||||
mw.logger.ErrorContext(ctx, "retrieving user from request", slogutil.KeyError, err)
|
||||
}
|
||||
|
||||
if u != nil {
|
||||
if path == "/login.html" {
|
||||
http.Redirect(w, r, "/", http.StatusFound)
|
||||
|
||||
if mw.handleAuthenticatedUser(ctx, w, r, h, path) {
|
||||
return
|
||||
}
|
||||
|
||||
h.ServeHTTP(w, r.WithContext(withWebUser(ctx, u)))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if isPublicResource(path) {
|
||||
h.ServeHTTP(w, r)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if path == "/" || path == "/index.html" {
|
||||
http.Redirect(w, r, "login.html", http.StatusFound)
|
||||
|
||||
if mw.handlePublicAccess(w, r, h, path) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -407,6 +386,58 @@ func (mw *authMiddlewareDefault) Wrap(h http.Handler) (wrapped http.Handler) {
|
||||
})
|
||||
}
|
||||
|
||||
// handleAuthenticatedUser tries to get user from request and processes request
|
||||
// if user was successfully authenticated. Returns true if request was handled.
|
||||
func (mw *authMiddlewareDefault) handleAuthenticatedUser(
|
||||
ctx context.Context,
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
h http.Handler,
|
||||
path string,
|
||||
) (ok bool) {
|
||||
u, err := mw.userFromRequest(ctx, r)
|
||||
if err != nil {
|
||||
mw.logger.ErrorContext(ctx, "retrieving user from request", slogutil.KeyError, err)
|
||||
}
|
||||
|
||||
if u == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if path == "/login.html" {
|
||||
http.Redirect(w, r, "/", http.StatusFound)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
h.ServeHTTP(w, r.WithContext(withWebUser(ctx, u)))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// handlePublicAccess handles request if user is trying to access public or root
|
||||
// pages.
|
||||
func (mw *authMiddlewareDefault) handlePublicAccess(
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
h http.Handler,
|
||||
path string,
|
||||
) (ok bool) {
|
||||
if isPublicResource(path) {
|
||||
h.ServeHTTP(w, r)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if path == "/" || path == "/index.html" {
|
||||
http.Redirect(w, r, "login.html", http.StatusFound)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// needsAuthentication returns true if there are stored web users and requests
|
||||
// should be authenticated first.
|
||||
func (mw *authMiddlewareDefault) needsAuthentication(ctx context.Context) (ok bool) {
|
||||
|
||||
@@ -228,56 +228,60 @@ func finishUpdate(
|
||||
cleanup(ctx)
|
||||
cleanupAlways()
|
||||
|
||||
var err error
|
||||
if runtime.GOOS == "windows" {
|
||||
if runningAsService {
|
||||
// NOTE: We can't restart the service via "kardianos/service"
|
||||
// package, because it kills the process first we can't start a new
|
||||
// instance, because Windows doesn't allow it.
|
||||
//
|
||||
// TODO(a.garipov): Recheck the claim above.
|
||||
var cmd executil.Command
|
||||
cmd, err = cmdCons.New(ctx, &executil.CommandConfig{
|
||||
Path: "cmd",
|
||||
Args: []string{"/c", "net stop AdGuardHome & net start AdGuardHome"},
|
||||
})
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("constructing cmd: %w", err))
|
||||
}
|
||||
|
||||
err = cmd.Start(ctx)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("restarting service: %w", err))
|
||||
}
|
||||
|
||||
os.Exit(osutil.ExitCodeSuccess)
|
||||
}
|
||||
|
||||
l.InfoContext(ctx, "restarting", "exec_path", execPath, "args", os.Args[1:])
|
||||
|
||||
var cmd executil.Command
|
||||
cmd, err = cmdCons.New(ctx, &executil.CommandConfig{
|
||||
Path: execPath,
|
||||
Args: os.Args[1:],
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
})
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("constructing cmd: %w", err))
|
||||
}
|
||||
|
||||
err = cmd.Start(ctx)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("restarting: %w", err))
|
||||
}
|
||||
finalizeWindowsUpdate(ctx, l, cmdCons, execPath, runningAsService)
|
||||
|
||||
os.Exit(osutil.ExitCodeSuccess)
|
||||
}
|
||||
|
||||
var err error
|
||||
l.InfoContext(ctx, "restarting", "exec_path", execPath, "args", os.Args[1:])
|
||||
err = syscall.Exec(execPath, os.Args, os.Environ())
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("restarting: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
// finalizeWindowsUpdate completes an update procedure on windows. l and
|
||||
// cmdCons must not be nil.
|
||||
func finalizeWindowsUpdate(ctx context.Context,
|
||||
l *slog.Logger,
|
||||
cmdCons executil.CommandConstructor,
|
||||
execPath string,
|
||||
runningAsService bool,
|
||||
) {
|
||||
var commandConf *executil.CommandConfig
|
||||
|
||||
if runningAsService {
|
||||
// NOTE: We can't restart the service via "kardianos/service" package,
|
||||
// because it kills the process first we can't start a new instance,
|
||||
// because Windows doesn't allow it.
|
||||
//
|
||||
// TODO(a.garipov): Recheck the claim above.
|
||||
commandConf = &executil.CommandConfig{
|
||||
Path: "cmd",
|
||||
Args: []string{"/c", "net stop AdGuardHome & net start AdGuardHome"},
|
||||
}
|
||||
} else {
|
||||
commandConf = &executil.CommandConfig{
|
||||
Path: execPath,
|
||||
Args: os.Args[1:],
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
}
|
||||
}
|
||||
|
||||
l.InfoContext(ctx, "restarting", "exec_path", execPath, "args", os.Args[1:])
|
||||
|
||||
var cmd executil.Command
|
||||
cmd, err := cmdCons.New(ctx, commandConf)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("constructing cmd: %w", err))
|
||||
}
|
||||
|
||||
err = cmd.Start(ctx)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("restarting: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -695,100 +695,29 @@ func run(
|
||||
workDir string,
|
||||
confPath string,
|
||||
) {
|
||||
ls := getLogSettings(ctx, slogLogger, opts, workDir, confPath)
|
||||
|
||||
// Configure log level and output.
|
||||
err := configureLogger(ls, workDir)
|
||||
fatalOnError(err)
|
||||
|
||||
// Print the first message after logger is configured.
|
||||
slogLogger.InfoContext(ctx, "starting adguard home", "version", version.Full())
|
||||
slogLogger.DebugContext(ctx, "current working directory", "path", workDir)
|
||||
if opts.runningAsService {
|
||||
slogLogger.InfoContext(ctx, "adguard home is running as a service")
|
||||
}
|
||||
|
||||
aghtls.Init(ctx, slogLogger.With(slogutil.KeyPrefix, "aghtls"))
|
||||
initEnvironment(ctx, opts, slogLogger, workDir, confPath)
|
||||
|
||||
isFirstRun := detectFirstRun(ctx, slogLogger, workDir, confPath)
|
||||
|
||||
err = setupContext(ctx, slogLogger, opts, workDir, confPath, isFirstRun)
|
||||
fatalOnError(err)
|
||||
|
||||
err = configureOS(config)
|
||||
fatalOnError(err)
|
||||
|
||||
// Clients package uses filtering package's static data
|
||||
// (filtering.BlockedSvcKnown()), so we have to initialize filtering static
|
||||
// data first, but also to avoid relying on automatic Go init() function.
|
||||
filtering.InitModule(ctx, slogLogger)
|
||||
|
||||
confModifier := newDefaultConfigModifier(
|
||||
config,
|
||||
slogLogger.With(slogutil.KeyPrefix, "config_modifier"),
|
||||
workDir,
|
||||
confPath,
|
||||
)
|
||||
|
||||
mw := &webMw{}
|
||||
mux := http.NewServeMux()
|
||||
httpReg := aghhttp.NewDefaultRegistrar(mux, mw.wrap)
|
||||
|
||||
err = initContextClients(ctx, slogLogger, sigHdlr, confModifier, httpReg, workDir)
|
||||
fatalOnError(err)
|
||||
|
||||
tlsMgrLogger := slogLogger.With(slogutil.KeyPrefix, "tls_manager")
|
||||
|
||||
tlsMgr, err := newTLSManager(ctx, &tlsManagerConfig{
|
||||
logger: tlsMgrLogger,
|
||||
confModifier: confModifier,
|
||||
httpReg: httpReg,
|
||||
tlsSettings: config.TLS,
|
||||
servePlainDNS: config.DNS.ServePlainDNS,
|
||||
})
|
||||
if err != nil {
|
||||
tlsMgrLogger.ErrorContext(ctx, "initializing", slogutil.KeyError, err)
|
||||
confModifier.Apply(ctx)
|
||||
}
|
||||
|
||||
confModifier.setTLSManager(tlsMgr)
|
||||
|
||||
err = setupDNSFilteringConf(
|
||||
confModifier, tlsMgr := initFiltering(
|
||||
ctx,
|
||||
slogLogger,
|
||||
config.Filtering,
|
||||
tlsMgr,
|
||||
confModifier,
|
||||
httpReg,
|
||||
opts,
|
||||
isFirstRun,
|
||||
sigHdlr,
|
||||
workDir,
|
||||
confPath,
|
||||
httpReg,
|
||||
)
|
||||
fatalOnError(err)
|
||||
|
||||
err = setupOpts(opts)
|
||||
fatalOnError(err)
|
||||
|
||||
execPath, err := os.Executable()
|
||||
fatalOnError(errors.Annotate(err, "getting executable path: %w"))
|
||||
|
||||
updLogger := slogLogger.With(slogutil.KeyPrefix, "updater")
|
||||
upd, isCustomURL := newUpdater(ctx, updLogger, config, workDir, confPath, execPath)
|
||||
|
||||
// TODO(e.burkov): This could be made earlier, probably as the option's
|
||||
// effect.
|
||||
cmdlineUpdate(ctx, updLogger, opts, upd, tlsMgr, isFirstRun)
|
||||
|
||||
if !isFirstRun {
|
||||
// Save the updated config.
|
||||
err = config.write(ctx, slogLogger, nil, nil, workDir, confPath)
|
||||
fatalOnError(err)
|
||||
|
||||
if config.HTTPConfig.Pprof.Enabled {
|
||||
startPprof(slogLogger, config.HTTPConfig.Pprof.Port)
|
||||
}
|
||||
}
|
||||
upd, isCustomURL := initUpdate(ctx, slogLogger, opts, tlsMgr, isFirstRun, workDir, confPath)
|
||||
|
||||
dataDirPath := filepath.Join(workDir, dataDir)
|
||||
err = os.MkdirAll(dataDirPath, aghos.DefaultPermDir)
|
||||
err := os.MkdirAll(dataDirPath, aghos.DefaultPermDir)
|
||||
fatalOnError(errors.Annotate(err, "creating DNS data dir at %s: %w", dataDirPath))
|
||||
|
||||
auth, err := initUsers(ctx, slogLogger, workDir, opts.glinetMode)
|
||||
@@ -825,7 +754,31 @@ func run(
|
||||
fatalOnError(err)
|
||||
|
||||
if !isFirstRun {
|
||||
err = initDNS(ctx, slogLogger, tlsMgr, confModifier, httpReg, statsDir, querylogDir)
|
||||
runDNSServer(ctx, slogLogger, tlsMgr, confModifier, statsDir, querylogDir, httpReg)
|
||||
}
|
||||
|
||||
if !opts.noPermCheck {
|
||||
checkPermissions(ctx, slogLogger, workDir, confPath, dataDirPath, statsDir, querylogDir)
|
||||
}
|
||||
|
||||
web.start(ctx)
|
||||
|
||||
// Wait for other goroutines to complete their job.
|
||||
<-done
|
||||
}
|
||||
|
||||
// runDNSServer initializes and starts DNS and DHCP servers if this is not the
|
||||
// first run. httpReg, slogLogger, tlsMgr and confModifier must not be nil.
|
||||
func runDNSServer(
|
||||
ctx context.Context,
|
||||
slogLogger *slog.Logger,
|
||||
tlsMgr *tlsManager,
|
||||
confModifier *defaultConfigModifier,
|
||||
statsDir string,
|
||||
querylogDir string,
|
||||
httpReg *aghhttp.DefaultRegistrar,
|
||||
) {
|
||||
err := initDNS(ctx, slogLogger, tlsMgr, confModifier, httpReg, statsDir, querylogDir)
|
||||
fatalOnError(err)
|
||||
|
||||
tlsMgr.start(ctx)
|
||||
@@ -844,16 +797,139 @@ func run(
|
||||
slogLogger.ErrorContext(ctx, "starting dhcp server", slogutil.KeyError, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// initFiltering configures the core filtering and TLS subsystems. Returns a
|
||||
// configuration modifier and the initialized TLS manager. slogLogger, sigHdlr
|
||||
// and httpReg must not be nil.
|
||||
func initFiltering(
|
||||
ctx context.Context,
|
||||
slogLogger *slog.Logger,
|
||||
opts options,
|
||||
isFirstRun bool,
|
||||
sigHdlr *signalHandler,
|
||||
workDir string,
|
||||
confPath string,
|
||||
httpReg *aghhttp.DefaultRegistrar,
|
||||
) (confModifier *defaultConfigModifier, tlsMgr *tlsManager) {
|
||||
err := setupContext(ctx, slogLogger, opts, workDir, confPath, isFirstRun)
|
||||
fatalOnError(err)
|
||||
|
||||
err = configureOS(config)
|
||||
fatalOnError(err)
|
||||
|
||||
// Clients package uses filtering package's static data
|
||||
// (filtering.BlockedSvcKnown()), so we have to initialize filtering static
|
||||
// data first, but also to avoid relying on automatic Go init() function.
|
||||
filtering.InitModule(ctx, slogLogger)
|
||||
|
||||
confModifier = newDefaultConfigModifier(
|
||||
config,
|
||||
slogLogger.With(slogutil.KeyPrefix, "config_modifier"),
|
||||
workDir,
|
||||
confPath,
|
||||
)
|
||||
|
||||
err = initContextClients(ctx, slogLogger, sigHdlr, confModifier, httpReg, workDir)
|
||||
fatalOnError(err)
|
||||
|
||||
tlsMgrLogger := slogLogger.With(slogutil.KeyPrefix, "tls_manager")
|
||||
|
||||
tlsMgr, err = newTLSManager(ctx, &tlsManagerConfig{
|
||||
logger: tlsMgrLogger,
|
||||
confModifier: confModifier,
|
||||
httpReg: httpReg,
|
||||
tlsSettings: config.TLS,
|
||||
servePlainDNS: config.DNS.ServePlainDNS,
|
||||
})
|
||||
if err != nil {
|
||||
tlsMgrLogger.ErrorContext(ctx, "initializing", slogutil.KeyError, err)
|
||||
confModifier.Apply(ctx)
|
||||
}
|
||||
|
||||
if !opts.noPermCheck {
|
||||
checkPermissions(ctx, slogLogger, workDir, confPath, dataDirPath, statsDir, querylogDir)
|
||||
confModifier.setTLSManager(tlsMgr)
|
||||
|
||||
err = setupDNSFilteringConf(
|
||||
ctx,
|
||||
slogLogger,
|
||||
config.Filtering,
|
||||
tlsMgr,
|
||||
confModifier,
|
||||
httpReg,
|
||||
workDir,
|
||||
)
|
||||
fatalOnError(err)
|
||||
|
||||
err = setupOpts(opts)
|
||||
fatalOnError(err)
|
||||
|
||||
return confModifier, tlsMgr
|
||||
}
|
||||
|
||||
// initUpdate configures and runs update of this application. slogLogger and
|
||||
// tlsMgr must not be nil.
|
||||
func initUpdate(
|
||||
ctx context.Context,
|
||||
slogLogger *slog.Logger,
|
||||
opts options,
|
||||
tlsMgr *tlsManager,
|
||||
isFirstRun bool,
|
||||
workDir string,
|
||||
confPath string,
|
||||
) (upd *updater.Updater, isCustomURL bool) {
|
||||
execPath, err := os.Executable()
|
||||
fatalOnError(errors.Annotate(err, "getting executable path: %w"))
|
||||
|
||||
updLogger := slogLogger.With(slogutil.KeyPrefix, "updater")
|
||||
upd, isCustomURL = newUpdater(
|
||||
ctx,
|
||||
updLogger,
|
||||
config,
|
||||
workDir,
|
||||
confPath,
|
||||
execPath,
|
||||
)
|
||||
|
||||
// TODO(e.burkov): This could be made earlier, probably as the option's
|
||||
// effect.
|
||||
cmdlineUpdate(ctx, updLogger, opts, upd, tlsMgr, isFirstRun)
|
||||
|
||||
if !isFirstRun {
|
||||
// Save the updated config.
|
||||
err = config.write(ctx, slogLogger, nil, nil, workDir, confPath)
|
||||
fatalOnError(err)
|
||||
|
||||
if config.HTTPConfig.Pprof.Enabled {
|
||||
startPprof(slogLogger, config.HTTPConfig.Pprof.Port)
|
||||
}
|
||||
}
|
||||
|
||||
web.start(ctx)
|
||||
return upd, isCustomURL
|
||||
}
|
||||
|
||||
// Wait for other goroutines to complete their job.
|
||||
<-done
|
||||
// initEnvironment inits working environment. opts and slogLogger must not be
|
||||
// nil.
|
||||
func initEnvironment(
|
||||
ctx context.Context,
|
||||
opts options,
|
||||
slogLogger *slog.Logger,
|
||||
workDir,
|
||||
confPath string,
|
||||
) {
|
||||
ls := getLogSettings(ctx, slogLogger, opts, workDir, confPath)
|
||||
|
||||
// Configure log level and output.
|
||||
err := configureLogger(ls, workDir)
|
||||
fatalOnError(err)
|
||||
|
||||
// Print the first message after logger is configured.
|
||||
slogLogger.InfoContext(ctx, "starting adguard home", "version", version.Full())
|
||||
slogLogger.DebugContext(ctx, "current working directory", "path", workDir)
|
||||
if opts.runningAsService {
|
||||
slogLogger.InfoContext(ctx, "adguard home is running as a service")
|
||||
}
|
||||
|
||||
aghtls.Init(ctx, slogLogger.With(slogutil.KeyPrefix, "aghtls"))
|
||||
}
|
||||
|
||||
// newUpdater creates a new AdGuard Home updater. l and conf must not be nil.
|
||||
|
||||
@@ -2,8 +2,10 @@ package home
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"iter"
|
||||
"net/netip"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -384,41 +386,89 @@ func printHelp(exec string) {
|
||||
|
||||
// parseCmdOpts parses the command-line arguments into options and effects.
|
||||
func parseCmdOpts(cmdName string, args []string) (o options, eff effect, err error) {
|
||||
// Don't use range since the loop changes the loop variable.
|
||||
argsLen := len(args)
|
||||
for i := 0; i < len(args); i++ {
|
||||
arg := args[i]
|
||||
isKnown := false
|
||||
for _, opt := range cmdLineOpts {
|
||||
isKnown = argMatches(opt, arg)
|
||||
if !isKnown {
|
||||
continue
|
||||
next, stop := iter.Pull2(slices.All(args))
|
||||
defer stop()
|
||||
|
||||
for i, arg, ok := next(); ok; i, arg, ok = next() {
|
||||
o, eff, err = parseArg(cmdName, next, o, eff, arg)
|
||||
if err != nil {
|
||||
return o, eff, fmt.Errorf("parsing arg at index %d: %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
return o, eff, nil
|
||||
}
|
||||
|
||||
// parseArg parses command-line argument into options and effects. next and
|
||||
// eff must not be nil.
|
||||
func parseArg(
|
||||
cmdName string,
|
||||
next func() (int, string, bool),
|
||||
o options,
|
||||
eff effect,
|
||||
arg string,
|
||||
) (newOpt options, newEff effect, err error) {
|
||||
opt, found := findMatchingOpt(arg)
|
||||
if !found {
|
||||
return o, eff, fmt.Errorf("unknown option %s", arg)
|
||||
}
|
||||
|
||||
if opt.updateWithValue != nil {
|
||||
i++
|
||||
if i >= argsLen {
|
||||
return o, eff, fmt.Errorf("got %s without argument", arg)
|
||||
return applyOptWithValue(opt, next, o, eff, arg)
|
||||
}
|
||||
|
||||
o, err = opt.updateWithValue(o, args[i])
|
||||
} else {
|
||||
o, eff, err = updateOptsNoValue(o, eff, opt, cmdName)
|
||||
}
|
||||
return applyOptNoValue(opt, cmdName, o, eff, arg)
|
||||
}
|
||||
|
||||
// applyOptNoValue applies option with no value. eff must not be
|
||||
// nil.
|
||||
func applyOptNoValue(
|
||||
opt cmdLineOpt,
|
||||
cmdName string,
|
||||
o options,
|
||||
eff effect,
|
||||
arg string,
|
||||
) (newOpt options, newEff effect, err error) {
|
||||
newOpts, newEff, err := updateOptsNoValue(o, eff, opt, cmdName)
|
||||
if err != nil {
|
||||
return o, eff, fmt.Errorf("applying option %s: %w", arg, err)
|
||||
}
|
||||
|
||||
break
|
||||
return newOpts, newEff, nil
|
||||
}
|
||||
|
||||
// applyOptWithValue applies argument with value. next and eff must not
|
||||
// be nil.
|
||||
func applyOptWithValue(
|
||||
opt cmdLineOpt,
|
||||
next func() (int, string, bool),
|
||||
o options,
|
||||
eff effect,
|
||||
arg string,
|
||||
) (newOpt options, newEff effect, err error) {
|
||||
_, val, ok := next()
|
||||
if !ok {
|
||||
return o, eff, fmt.Errorf("got %s without argument", arg)
|
||||
}
|
||||
|
||||
if !isKnown {
|
||||
return o, eff, fmt.Errorf("unknown option %s", arg)
|
||||
newOpts, err := opt.updateWithValue(o, val)
|
||||
if err != nil {
|
||||
return o, eff, fmt.Errorf("applying option %s: %w", arg, err)
|
||||
}
|
||||
|
||||
return newOpts, eff, nil
|
||||
}
|
||||
|
||||
// findMatchingOpt returns cmdLineOpt which matches the given arg. ok indicates
|
||||
// whether the appropriate option was found.
|
||||
func findMatchingOpt(arg string) (opt cmdLineOpt, ok bool) {
|
||||
for _, opt := range cmdLineOpts {
|
||||
if argMatches(opt, arg) {
|
||||
return opt, true
|
||||
}
|
||||
}
|
||||
|
||||
return o, eff, err
|
||||
return cmdLineOpt{}, false
|
||||
}
|
||||
|
||||
// argMatches returns true if arg matches command-line option opt.
|
||||
|
||||
@@ -390,16 +390,11 @@ func handleServiceInstallCommand(
|
||||
}
|
||||
}
|
||||
|
||||
// handleServiceUninstallCommand handles service "uninstall" command.
|
||||
// handleServiceUninstallCommand handles service "uninstall" command. l and s
|
||||
// must not be nil.
|
||||
func handleServiceUninstallCommand(ctx context.Context, l *slog.Logger, s service.Service) {
|
||||
if aghos.IsOpenWrt() {
|
||||
// On OpenWrt it is important to run disable command first
|
||||
// as it will remove the symlink
|
||||
_, err := runInitdCommand(ctx, "disable")
|
||||
if err != nil {
|
||||
l.ErrorContext(ctx, "running init disable", slogutil.KeyError, err)
|
||||
os.Exit(osutil.ExitCodeFailure)
|
||||
}
|
||||
handleOpenWrtUninstall(ctx, l)
|
||||
}
|
||||
|
||||
if err := svcAction(ctx, l, s, "stop"); err != nil {
|
||||
@@ -408,10 +403,30 @@ func handleServiceUninstallCommand(ctx context.Context, l *slog.Logger, s servic
|
||||
|
||||
if err := svcAction(ctx, l, s, "uninstall"); err != nil {
|
||||
l.ErrorContext(ctx, "executing action uninstall", slogutil.KeyError, err)
|
||||
|
||||
os.Exit(osutil.ExitCodeFailure)
|
||||
}
|
||||
|
||||
if runtime.GOOS == "darwin" {
|
||||
handleDarwinUninstall(ctx, l)
|
||||
}
|
||||
}
|
||||
|
||||
// handleOpenWrtUninstall handles service "uninstall" command for OpenWrt.
|
||||
// Exits on error. l must not be nil.
|
||||
func handleOpenWrtUninstall(ctx context.Context, l *slog.Logger) {
|
||||
// On OpenWrt it is important to run disable command first as it will remove
|
||||
// the symlink.
|
||||
_, err := runInitdCommand(ctx, "disable")
|
||||
if err != nil {
|
||||
l.ErrorContext(ctx, "running init disable", slogutil.KeyError, err)
|
||||
os.Exit(osutil.ExitCodeFailure)
|
||||
}
|
||||
}
|
||||
|
||||
// handleDarwinUninstall handles service "uninstall" command for Darwin. l
|
||||
// must not be nil.
|
||||
func handleDarwinUninstall(ctx context.Context, l *slog.Logger) {
|
||||
// Remove log files on cleanup and log errors.
|
||||
err := os.Remove(launchdStdoutPath)
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
@@ -422,7 +437,6 @@ func handleServiceUninstallCommand(ctx context.Context, l *slog.Logger, s servic
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
l.WarnContext(ctx, "removing stderr file", slogutil.KeyError, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// configureService defines additional settings of the service
|
||||
|
||||
@@ -318,26 +318,24 @@ func (web *webAPI) close(ctx context.Context) {
|
||||
web.logger.InfoContext(ctx, "stopped http server")
|
||||
}
|
||||
|
||||
// tlsServerLoop implements retry logic for http server start.
|
||||
func (web *webAPI) tlsServerLoop(ctx context.Context) {
|
||||
defer slogutil.RecoverAndExit(ctx, web.logger, osutil.ExitCodeFailure)
|
||||
|
||||
for {
|
||||
web.httpsServer.cond.L.Lock()
|
||||
if web.httpsServer.inShutdown {
|
||||
web.httpsServer.cond.L.Unlock()
|
||||
break
|
||||
}
|
||||
|
||||
// this mechanism doesn't let us through until all conditions are met
|
||||
for !web.httpsServer.enabled { // sleep until necessary data is supplied
|
||||
web.httpsServer.cond.Wait()
|
||||
if web.httpsServer.inShutdown {
|
||||
web.httpsServer.cond.L.Unlock()
|
||||
shouldContinue := web.serveTLS(ctx)
|
||||
if !shouldContinue {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
web.httpsServer.cond.L.Unlock()
|
||||
// serveTLS initializes and starts the HTTPS server. Returns true when next
|
||||
// retry is necessary.
|
||||
func (web *webAPI) serveTLS(ctx context.Context) (next bool) {
|
||||
if !web.waitForTLSReady() {
|
||||
return false
|
||||
}
|
||||
|
||||
var portHTTPS uint16
|
||||
func() {
|
||||
@@ -381,7 +379,29 @@ func (web *webAPI) tlsServerLoop(ctx context.Context) {
|
||||
cleanupAlways()
|
||||
panic(fmt.Errorf("https: %w", err))
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// waitForTLSReady blocks until the HTTPS server is enabled or a shutdown signal
|
||||
// is received. Returns true when server is ready.
|
||||
func (web *webAPI) waitForTLSReady() (ok bool) {
|
||||
web.httpsServer.cond.L.Lock()
|
||||
defer web.httpsServer.cond.L.Unlock()
|
||||
|
||||
if web.httpsServer.inShutdown {
|
||||
return false
|
||||
}
|
||||
|
||||
// this mechanism doesn't let us through until all conditions are met
|
||||
for !web.httpsServer.enabled { // sleep until necessary data is supplied
|
||||
web.httpsServer.cond.Wait()
|
||||
if web.httpsServer.inShutdown {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (web *webAPI) mustStartHTTP3(ctx context.Context, address string) {
|
||||
|
||||
@@ -172,10 +172,6 @@ run_linter gocognit --over='20' \
|
||||
./internal/querylog/ \
|
||||
;
|
||||
|
||||
run_linter gocognit --over='19' \
|
||||
./internal/home/ \
|
||||
;
|
||||
|
||||
run_linter gocognit --over='14' \
|
||||
./internal/dhcpd \
|
||||
;
|
||||
@@ -195,6 +191,7 @@ run_linter gocognit --over='10' \
|
||||
./internal/dhcpsvc \
|
||||
./internal/dnsforward/ \
|
||||
./internal/filtering/ \
|
||||
./internal/home/ \
|
||||
./internal/ipset \
|
||||
./internal/next/ \
|
||||
./internal/rdns/ \
|
||||
|
||||
Reference in New Issue
Block a user