// Shared env-file handling for the installers (Windows and Linux). package service import ( "fmt" "os" "strings" "gpu-turnstile/internal/config" ) // syncEnvFile ensures the installed env file at path is a current // installer-written sample. A missing file is created; a file without a // CFG_VER line (hand-written or from before versioning) is invalid and // replaced by a fresh sample after a .bak backup; a file written by an // older version gets newly added settings appended via config.SyncSample. // logPath activates the LOG_FILE line (Windows); empty leaves it commented. func syncEnvFile(path, version, logPath string) error { data, err := os.ReadFile(path) switch { case os.IsNotExist(err): return writeEnvFile(path, config.SampleEnv(version, logPath)) case err != nil: return fmt.Errorf("read %s: %w", path, err) } values, perr := config.ParseEnvFile(strings.NewReader(string(data))) if perr == nil && values["CFG_VER"] != "" { synced, changed := config.SyncSample(string(data), version, logPath) if !changed { return nil } return writeEnvFile(path, synced) } // No readable CFG_VER: the file is invalid — keep a backup and start // from a fresh sample. if err := os.WriteFile(path+".bak", data, 0o644); err != nil { return fmt.Errorf("back up %s: %w", path, err) } return writeEnvFile(path, config.SampleEnv(version, logPath)) } func writeEnvFile(path, content string) error { if err := os.WriteFile(path, []byte(content), 0o644); err != nil { return fmt.Errorf("write %s: %w", path, err) } return nil } // configuredValue reads one key from the env file the service will load, so // the installers can adapt the sandbox to it (ACL grants, unit directives). // "" when unset or unreadable. func configuredValue(configPath, key string) string { f, err := os.Open(configPath) if err != nil { return "" } defer f.Close() values, err := config.ParseEnvFile(f) if err != nil { return "" } return values[key] }