diff --git a/internal/home/config.go b/internal/home/config.go index 1619d083..b9200239 100644 --- a/internal/home/config.go +++ b/internal/home/config.go @@ -456,20 +456,25 @@ var config = &configuration{ Theme: ThemeAuto, } -// getConfigFilename returns path to the current config file -func (c *configuration) getConfigFilename() string { - configFile, err := filepath.EvalSymlinks(Context.configFilename) +// configFilePath returns the absolute path to the symlink-evaluated path to the +// current config file. +func configFilePath() (confPath string) { + confPath, err := filepath.EvalSymlinks(Context.confFilePath) if err != nil { - if !errors.Is(err, os.ErrNotExist) { - log.Error("unexpected error while config file path evaluation: %s", err) + confPath = Context.confFilePath + logFunc := log.Error + if errors.Is(err, os.ErrNotExist) { + logFunc = log.Debug } - configFile = Context.configFilename - } - if !filepath.IsAbs(configFile) { - configFile = filepath.Join(Context.workDir, configFile) + + logFunc("evaluating config path: %s; using %q", err, confPath) } - return configFile + if !filepath.IsAbs(confPath) { + confPath = filepath.Join(Context.workDir, confPath) + } + + return confPath } // validateBindHosts returns error if any of binding hosts from configuration is @@ -510,7 +515,10 @@ func parseConfig() (err error) { // Don't wrap the error, because it's informative enough as is. return err } else if upgraded { - err = maybe.WriteFile(config.getConfigFilename(), config.fileData, 0o644) + confPath := configFilePath() + log.Debug("writing config file %q after config upgrade", confPath) + + err = maybe.WriteFile(confPath, config.fileData, 0o644) if err != nil { return fmt.Errorf("writing new config: %w", err) } @@ -531,12 +539,8 @@ func parseConfig() (err error) { config.DNS.UpstreamTimeout = timeutil.Duration{Duration: dnsforward.DefaultTimeout} } - err = setContextTLSCipherIDs() - if err != nil { - return err - } - - return nil + // Do not wrap the error because it's informative enough as is. + return setContextTLSCipherIDs() } // validateConfig returns error if the configuration is invalid. @@ -600,11 +604,11 @@ func readConfigFile() (fileData []byte, err error) { return config.fileData, nil } - name := config.getConfigFilename() - log.Debug("reading config file: %s", name) + confPath := configFilePath() + log.Debug("reading config file %q", confPath) // Do not wrap the error because it's informative enough as is. - return os.ReadFile(name) + return os.ReadFile(confPath) } // Saves configuration to the YAML file and also saves the user filter contents to a file @@ -668,8 +672,8 @@ func (c *configuration) write() (err error) { config.Clients.Persistent = Context.clients.forConfig() - configFile := config.getConfigFilename() - log.Debug("writing config file %q", configFile) + confPath := configFilePath() + log.Debug("writing config file %q", confPath) buf := &bytes.Buffer{} enc := yaml.NewEncoder(buf) @@ -680,7 +684,7 @@ func (c *configuration) write() (err error) { return fmt.Errorf("generating config file: %w", err) } - err = maybe.WriteFile(configFile, buf.Bytes(), 0o644) + err = maybe.WriteFile(confPath, buf.Bytes(), 0o644) if err != nil { return fmt.Errorf("writing config file: %w", err) } diff --git a/internal/home/home.go b/internal/home/home.go index 90949f07..4cc91716 100644 --- a/internal/home/home.go +++ b/internal/home/home.go @@ -40,7 +40,6 @@ import ( "github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/osutil" - "github.com/AdguardTeam/golibs/stringutil" ) // Global context @@ -68,11 +67,14 @@ type homeContext struct { // Runtime properties // -- - configFilename string // Config filename (can be overridden via the command line arguments) - workDir string // Location of our directory, used to protect against CWD being somewhere else - pidFileName string // PID file name. Empty if no PID file was created. - controlLock sync.Mutex - tlsRoots *x509.CertPool // list of root CAs for TLSv1.2 + // confFilePath is the configuration file path as set by default or from the + // command-line options. + confFilePath string + + workDir string // Location of our directory, used to protect against CWD being somewhere else + pidFileName string // PID file name. Empty if no PID file was created. + controlLock sync.Mutex + tlsRoots *x509.CertPool // list of root CAs for TLSv1.2 // tlsCipherIDs are the ID of the cipher suites that AdGuard Home must use. tlsCipherIDs []uint16 @@ -575,6 +577,9 @@ func run(opts options, clientBuildFS fs.FS, done chan struct{}) { Path: path.Join("adguardhome", version.Channel(), "version.json"), } + confPath := configFilePath() + log.Debug("using config path %q for updater", confPath) + upd := updater.NewUpdater(&updater.Config{ Client: config.Filtering.HTTPClient, Version: version.Version(), @@ -584,7 +589,7 @@ func run(opts options, clientBuildFS fs.FS, done chan struct{}) { GOARM: version.GOARM(), GOMIPS: version.GOMIPS(), WorkDir: Context.workDir, - ConfName: config.getConfigFilename(), + ConfName: confPath, ExecPath: execPath, VersionCheckURL: u.String(), }) @@ -748,7 +753,16 @@ func writePIDFile(fn string) bool { // initConfigFilename sets up context config file path. This file path can be // overridden by command-line arguments, or is set to default. func initConfigFilename(opts options) { - Context.configFilename = stringutil.Coalesce(opts.confFilename, "AdGuardHome.yaml") + confPath := opts.confFilename + if confPath == "" { + Context.confFilePath = "AdGuardHome.yaml" + + return + } + + log.Debug("config path overridden to %q from cmdline", confPath) + + Context.confFilePath = confPath } // initWorkingDir initializes the workDir. If no command-line arguments are @@ -906,16 +920,23 @@ func printHTTPAddresses(proto string) { } } -// ------------------- -// first run / install -// ------------------- -func detectFirstRun() bool { - configfile := Context.configFilename - if !filepath.IsAbs(configfile) { - configfile = filepath.Join(Context.workDir, Context.configFilename) +// detectFirstRun returns true if this is the first run of AdGuard Home. +func detectFirstRun() (ok bool) { + confPath := Context.confFilePath + if !filepath.IsAbs(confPath) { + confPath = filepath.Join(Context.workDir, Context.confFilePath) } - _, err := os.Stat(configfile) - return errors.Is(err, os.ErrNotExist) + + _, err := os.Stat(confPath) + if err == nil { + return false + } else if errors.Is(err, os.ErrNotExist) { + return true + } + + log.Error("detecting first run: %s; considering first run", err) + + return true } // jsonError is a generic JSON error response. diff --git a/internal/home/log.go b/internal/home/log.go index 28114800..c0c79fd5 100644 --- a/internal/home/log.go +++ b/internal/home/log.go @@ -75,6 +75,8 @@ func getLogSettings(opts options) (ls *logSettings) { if opts.verbose { ls.Verbose = true } + + // TODO(a.garipov): Use cmp.Or in Go 1.22. ls.File = stringutil.Coalesce(opts.logFile, ls.File) if opts.runningAsService && ls.File == "" && runtime.GOOS == "windows" { diff --git a/internal/home/service.go b/internal/home/service.go index 1a80ca07..727b8732 100644 --- a/internal/home/service.go +++ b/internal/home/service.go @@ -227,12 +227,15 @@ func handleServiceControlAction( runOpts := opts runOpts.serviceControlAction = "run" + args := optsToArgs(runOpts) + log.Debug("service: using args %q", args) + svcConfig := &service.Config{ Name: serviceName, DisplayName: serviceDisplayName, Description: serviceDescription, WorkingDirectory: pwd, - Arguments: optsToArgs(runOpts), + Arguments: args, } configureService(svcConfig)