Pull request 2710: AGDNS-4214-rm-pid-file-name
Some checks are pending
build / test (macOS-latest) (push) Waiting to run
build / test (ubuntu-latest) (push) Waiting to run
build / test (windows-latest) (push) Waiting to run
build / build-release (push) Blocked by required conditions
build / notify (push) Blocked by required conditions
lint / go-lint (push) Waiting to run
lint / eslint (push) Waiting to run
lint / notify (push) Blocked by required conditions

Squashed commit of the following:

commit 6694e7c986fcf088d93efca1a4093f32af168878
Merge: 45816ff20 e3d638007
Author: f.setrakov <f.setrakov@adguard.com>
Date:   Mon Jul 20 17:03:45 2026 +0300

    Merge branch 'master' into AGDNS-4214-rm-pid-file-name

commit 45816ff2084aecb33f81d4be47bdae45f0fdc07f
Merge: 615f0f889 5a3f84a2f
Author: f.setrakov <f.setrakov@adguard.com>
Date:   Mon Jul 20 15:56:05 2026 +0300

    Merge branch 'master' into AGDNS-4214-rm-pid-file-name

commit 615f0f8899
Author: f.setrakov <f.setrakov@adguard.com>
Date:   Thu Jul 16 17:23:16 2026 +0300

    home: disable pid file on cli update

commit c92198f15e
Author: f.setrakov <f.setrakov@adguard.com>
Date:   Tue Jul 14 17:41:30 2026 +0300

    home: imp code

commit bba301587d
Author: f.setrakov <f.setrakov@adguard.com>
Date:   Tue Jul 14 14:52:10 2026 +0300

    home: rm pid file name from global ctx
This commit is contained in:
Fedor Setrakov 2026-07-20 14:10:13 +00:00
parent e3d638007a
commit 758376ee14
4 changed files with 59 additions and 40 deletions

View file

@ -170,6 +170,7 @@ func (web *webAPI) handleUpdate(w http.ResponseWriter, r *http.Request) {
web.logger,
web.cmdCons,
execPath,
web.pidFilePath,
web.conf.runningAsService,
)
}
@ -226,14 +227,15 @@ func finishUpdate(
l *slog.Logger,
cmdCons executil.CommandConstructor,
execPath string,
pidFilePath string,
runningAsService bool,
) {
defer slogutil.RecoverAndExit(ctx, l, osutil.ExitCodeFailure)
l.InfoContext(ctx, "stopping all tasks")
cleanup(ctx)
cleanupAlways()
cleanup(ctx, l)
cleanupAlways(ctx, l, pidFilePath)
if runtime.GOOS == "windows" {
finalizeWindowsUpdate(ctx, l, cmdCons, execPath, runningAsService)

View file

@ -66,10 +66,6 @@ type homeContext struct {
// configuration files, for example /etc/hosts.
etcHosts *aghnet.HostsContainer
// Runtime properties
// --
pidFileName string // PID file name. Empty if no PID file was created.
controlLock sync.Mutex
}
@ -130,12 +126,14 @@ func Main(clientBuildFS fs.FS) {
signals := make(chan os.Signal, 1)
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
pidFilePath := setPIDFilePath(opts)
sigHdlrLogger := baseLogger.With(slogutil.KeyPrefix, "signalhdlr")
sigHdlr := newSignalHandler(sigHdlrLogger, signals, func(ctx context.Context) {
defer close(done)
cleanup(ctx)
cleanupAlways()
cleanup(ctx, sigHdlrLogger)
cleanupAlways(ctx, sigHdlrLogger, pidFilePath)
if !opts.glinetMode {
return
@ -164,6 +162,7 @@ func Main(clientBuildFS fs.FS) {
sigHdlr,
workDir,
confPath,
pidFilePath,
)
if err != nil {
svcLogger.ErrorContext(ctx, "action failed", slogutil.KeyError, err)
@ -174,7 +173,7 @@ func Main(clientBuildFS fs.FS) {
}
// run the protection
run(ctx, baseLogger, opts, clientBuildFS, glTokenFileRoot, done, sigHdlr, workDir, confPath)
run(ctx, baseLogger, opts, clientBuildFS, glTokenFileRoot, done, sigHdlr, workDir, confPath, pidFilePath)
}
// setupContext initializes [globalContext] fields. It also reads and upgrades
@ -319,21 +318,6 @@ func setupHostsContainer(ctx context.Context, baseLogger *slog.Logger) (err erro
return hostsWatcher.Start(ctx)
}
// setupOpts sets up command-line options.
func setupOpts(opts options) (err error) {
err = setupBindOpts(opts)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
if len(opts.pidFile) != 0 && writePIDFile(opts.pidFile) {
globalContext.pidFileName = opts.pidFile
}
return nil
}
// initContextClients initializes Context clients and related fields. All
// arguments must not be nil.
func initContextClients(
@ -380,6 +364,16 @@ func initContextClients(
)
}
// setPIDFilePath writes the PID value to a file and returns its path, if the
// PID file option is specified.
func setPIDFilePath(opts options) (pidFilePath string) {
if opts.pidFile != "" && !opts.performUpdate && writePIDFile(opts.pidFile) {
pidFilePath = opts.pidFile
}
return pidFilePath
}
// setupBindOpts overrides bind host/port from the opts.
func setupBindOpts(opts options) (err error) {
bindAddr := opts.bindAddr
@ -649,6 +643,9 @@ type webConfig struct {
// confPath is a config path.
confPath string
// pidFilePath is a path to a PID file.
pidFilePath string
// isCustomUpdURL defines if updater should use custom url.
isCustomUpdURL bool
@ -699,6 +696,7 @@ func newWeb(ctx context.Context, conf *webConfig) (web *webAPI, err error) {
WriteTimeout: writeTimeout,
defaultWebPort: webPort,
pidFilePath: conf.pidFilePath,
firstRun: conf.isFirstRun,
disableUpdate: disableUpdate,
@ -766,6 +764,7 @@ func run(
sigHdlr *signalHandler,
workDir string,
confPath string,
pidFilePath string,
) {
aghtls.Init(ctx, baseLogger.With(slogutil.KeyPrefix, "aghtls"))
@ -810,7 +809,7 @@ func run(
)
fatalOnError(err)
err = setupOpts(opts)
err = setupBindOpts(opts)
fatalOnError(err)
upd, isCustomURL := initUpdate(ctx, baseLogger, opts, tlsMgr, isFirstRun, workDir, confPath)
@ -836,6 +835,7 @@ func run(
httpReg: httpReg,
workDir: workDir,
confPath: confPath,
pidFilePath: pidFilePath,
isCustomUpdURL: isCustomURL,
isFirstRun: isFirstRun,
}
@ -1214,9 +1214,9 @@ func initWorkingDir(opts options) (workDir string, err error) {
return workDir, nil
}
// cleanup stops and resets all the modules.
func cleanup(ctx context.Context) {
log.Info("stopping AdGuard Home")
// cleanup stops and resets all the modules. l must not be nil.
func cleanup(ctx context.Context, l *slog.Logger) {
l.InfoContext(ctx, "stopping adguard home")
if globalContext.web != nil {
globalContext.web.close(ctx)
@ -1225,30 +1225,33 @@ func cleanup(ctx context.Context) {
err := stopDNSServer(ctx)
if err != nil {
log.Error("stopping dns server: %s", err)
l.ErrorContext(ctx, "stopping dns server", slogutil.KeyError, err)
}
if globalContext.dhcpServer != nil {
err = globalContext.dhcpServer.Stop()
if err != nil {
log.Error("stopping dhcp server: %s", err)
l.ErrorContext(ctx, "stopping dhcp server", slogutil.KeyError, err)
}
}
if globalContext.etcHosts != nil {
if err = globalContext.etcHosts.Close(); err != nil {
log.Error("closing hosts container: %s", err)
l.ErrorContext(ctx, "closing hosts container", slogutil.KeyError, err)
}
}
}
// This function is called before application exits
func cleanupAlways() {
if len(globalContext.pidFileName) != 0 {
_ = os.Remove(globalContext.pidFileName)
// cleanupAlways is called on application exit. l must not be nil.
func cleanupAlways(ctx context.Context, l *slog.Logger, pidFilePath string) {
if pidFilePath != "" {
err := os.Remove(pidFilePath)
if err != nil {
l.ErrorContext(ctx, "removing pid file", slogutil.KeyError, err)
}
}
log.Info("stopped")
l.InfoContext(ctx, "stopped")
}
func exitWithError() {

View file

@ -44,12 +44,13 @@ type program struct {
gliNetTokenRoot *os.Root
workDir string
confPath string
pidFilePath string
}
// type check
var _ service.Interface = (*program)(nil)
// Start implements service.Interface interface for *program.
// Start implements [service.Interface] interface for *program.
func (p *program) Start(_ service.Service) (err error) {
// Start should not block. Do the actual work async.
args := p.opts
@ -65,6 +66,7 @@ func (p *program) Start(_ service.Service) (err error) {
p.sigHdlr,
p.workDir,
p.confPath,
p.pidFilePath,
)
return nil
@ -159,6 +161,7 @@ func handleServiceControlAction(
sigHdlr *signalHandler,
workDir string,
confPath string,
pidFilePath string,
) (err error) {
actionName := opts.serviceControlAction
l.InfoContext(ctx, version.Full())
@ -190,6 +193,7 @@ func handleServiceControlAction(
gliNetTokenRoot: gliNetTokenRoot,
workDir: workDir,
confPath: confPath,
pidFilePath: pidFilePath,
}
return p.handleRun(ctx, baseLogger, runOpts)

View file

@ -89,6 +89,9 @@ type webAPIConfig struct {
// confPath is the configuration file path.
confPath string
// pidFilePath is a path to a PID file.
pidFilePath string
// ReadTimeout is an option to pass to http.Server for setting an
// appropriate field.
ReadTimeout time.Duration
@ -234,6 +237,9 @@ type webAPI struct {
// TODO(d.kolyshev): Make it a pointer.
httpsServer httpsServer
// pidFilePath is used for cleanup.
pidFilePath string
// startTime is the start time of the web API server in Unix milliseconds.
startTime time.Time
}
@ -254,6 +260,7 @@ func newWebAPI(ctx context.Context, conf *webAPIConfig) (w *webAPI) {
baseLogger: conf.baseLogger,
tlsManager: conf.tlsManager,
auth: conf.auth,
pidFilePath: conf.pidFilePath,
startTime: time.Now(),
}
@ -374,7 +381,8 @@ func (web *webAPI) start(ctx context.Context) {
err := <-errs
if !errors.Is(err, http.ErrServerClosed) {
cleanupAlways()
cleanupAlways(ctx, logger, web.pidFilePath)
panic(err)
}
@ -478,7 +486,8 @@ func (web *webAPI) serveTLS(ctx context.Context) (next bool) {
logger.InfoContext(ctx, "starting https server")
err := web.httpsServer.server.ListenAndServeTLS("", "")
if !errors.Is(err, http.ErrServerClosed) {
cleanupAlways()
cleanupAlways(ctx, logger, web.pidFilePath)
panic(fmt.Errorf("https: %w", err))
}
@ -508,7 +517,8 @@ func (web *webAPI) mustStartHTTP3(ctx context.Context, address string) {
web.logger.DebugContext(ctx, "starting http/3 server")
err := web.httpsServer.server3.ListenAndServe()
if !errors.Is(err, http.ErrServerClosed) {
cleanupAlways()
cleanupAlways(ctx, logger, web.pidFilePath)
panic(fmt.Errorf("http3: %w", err))
}
}