mirror of
https://github.com/la5nta/pat
synced 2026-08-13 17:46:54 -04:00
192 lines
5.5 KiB
Go
192 lines
5.5 KiB
Go
// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved.
|
|
// Use of this source code is governed by the MIT-license that can be
|
|
// found in the LICENSE file.
|
|
|
|
package app
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
|
|
"github.com/kelseyhightower/envconfig"
|
|
"github.com/la5nta/pat/cfg"
|
|
"github.com/la5nta/pat/internal/buildinfo"
|
|
)
|
|
|
|
func LoadConfig(cfgPath string, fallback cfg.Config) (config cfg.Config, err error) {
|
|
config, err = ReadConfig(cfgPath)
|
|
switch {
|
|
case os.IsNotExist(err):
|
|
config = fallback
|
|
if err := WriteConfig(config, cfgPath); err != nil {
|
|
return config, err
|
|
}
|
|
case err != nil:
|
|
return config, err
|
|
}
|
|
|
|
// Environment variables overrides values from the config file
|
|
if err := envconfig.Process(buildinfo.AppName, &config); err != nil {
|
|
return config, err
|
|
}
|
|
// Environment variables for hamlib rigs (custom syntax not handled by envconfig)
|
|
if err := readRigsFromEnv(&config.HamlibRigs); err != nil {
|
|
return config, err
|
|
}
|
|
|
|
// Ensure the alias "telnet" exists
|
|
if config.ConnectAliases == nil {
|
|
config.ConnectAliases = make(map[string]string)
|
|
}
|
|
if _, exists := config.ConnectAliases["telnet"]; !exists {
|
|
config.ConnectAliases["telnet"] = cfg.DefaultConfig.ConnectAliases["telnet"]
|
|
}
|
|
|
|
// TODO: Remove after some release cycles (2023-05-21)
|
|
// Rewrite deprecated serial-tnc:// aliases to ax25-serial-tnc://
|
|
var deprecatedAliases []string
|
|
for k, v := range config.ConnectAliases {
|
|
if !strings.HasPrefix(v, MethodSerialTNCDeprecated+"://") {
|
|
continue
|
|
}
|
|
deprecatedAliases = append(deprecatedAliases, k)
|
|
config.ConnectAliases[k] = strings.Replace(v, MethodSerialTNCDeprecated, MethodAX25SerialTNC, 1)
|
|
}
|
|
if len(deprecatedAliases) > 0 {
|
|
log.Printf("Alias(es) %s uses deprecated transport scheme %s://. Please use %s:// instead.", strings.Join(deprecatedAliases, ", "), MethodSerialTNCDeprecated, MethodAX25SerialTNC)
|
|
}
|
|
|
|
// Ensure ServiceCodes has a default value
|
|
if len(config.ServiceCodes) == 0 {
|
|
config.ServiceCodes = cfg.DefaultConfig.ServiceCodes
|
|
}
|
|
|
|
// Ensure we have a default AX.25 engine
|
|
if config.AX25.Engine == "" {
|
|
config.AX25.Engine = cfg.DefaultAX25Engine()
|
|
}
|
|
|
|
// Ensure we have a default AGWPE config
|
|
if config.AGWPE == (cfg.AGWPEConfig{}) {
|
|
config.AGWPE = cfg.DefaultConfig.AGWPE
|
|
}
|
|
|
|
// Enforce minimum beacon intervals
|
|
if config.Ardop.BeaconInterval > 0 && config.Ardop.BeaconInterval < 10 {
|
|
config.Ardop.BeaconInterval = 10
|
|
}
|
|
if config.AX25.Beacon.Every > 0 && config.AX25.Beacon.Every < 10 {
|
|
config.AX25.Beacon.Every = 10
|
|
}
|
|
|
|
// Ensure we have a default AX.25 Linux config
|
|
if config.AX25Linux == (cfg.AX25LinuxConfig{}) {
|
|
config.AX25Linux = cfg.DefaultConfig.AX25Linux
|
|
}
|
|
// TODO: Remove after some release cycles (2023-04-30)
|
|
if v := config.AX25.AXPort; v != "" && v != config.AX25Linux.Port {
|
|
log.Println("Using deprecated configuration option ax25.port. Please set ax25_linux.port instead.")
|
|
config.AX25Linux.Port = v
|
|
}
|
|
|
|
// Ensure Pactor has a default value
|
|
if config.Pactor == (cfg.PactorConfig{}) {
|
|
config.Pactor = cfg.DefaultConfig.Pactor
|
|
}
|
|
|
|
// Ensure VARA FM and VARA HF has default values
|
|
if config.VaraHF.IsZero() {
|
|
config.VaraHF = cfg.DefaultConfig.VaraHF
|
|
}
|
|
if config.VaraFM.IsZero() {
|
|
config.VaraFM = cfg.DefaultConfig.VaraFM
|
|
}
|
|
|
|
// Ensure GPSd has a default value
|
|
if config.GPSd == (cfg.GPSdConfig{}) {
|
|
config.GPSd = cfg.DefaultConfig.GPSd
|
|
}
|
|
|
|
// Ensure SerialTNC has a default hbaud and serialbaud
|
|
if config.SerialTNC.HBaud == 0 {
|
|
config.SerialTNC.HBaud = cfg.DefaultConfig.SerialTNC.HBaud
|
|
}
|
|
if config.SerialTNC.SerialBaud == 0 {
|
|
config.SerialTNC.SerialBaud = cfg.DefaultConfig.SerialTNC.SerialBaud
|
|
}
|
|
// Compatibility for old type default 'Kenwood' (should be 'kenwood')
|
|
if v := config.SerialTNC.Type; v == "Kenwood" {
|
|
config.SerialTNC.Type = strings.ToLower(v)
|
|
}
|
|
|
|
// Ensure ARDOP.ConnectRequests has a default value
|
|
if config.Ardop.ConnectRequests == 0 {
|
|
config.Ardop.ConnectRequests = cfg.DefaultConfig.Ardop.ConnectRequests
|
|
}
|
|
|
|
return config, nil
|
|
}
|
|
|
|
// readRigsFromEnv reads hamlib rigs config from environment.
|
|
// Syntax: PAT_HAMLIB_RIGS_{rig name}_{ATTRIBUTE}
|
|
// _{ATTRIBUTE} is optional (defaults to _ADDRESS).
|
|
// Examples:
|
|
// - PAT_HAMLIB_RIGS_rig1_NETWORK=tcp
|
|
// - PAT_HAMLIB_RIGS_rig1_ADDRESS=localhost:8080
|
|
// - PAT_HAMLIB_RIGS_rig1_VFO=A
|
|
// - PAT_HAMLIB_RIGS_rig2=localhost:8080
|
|
func readRigsFromEnv(rigs *map[string]cfg.HamlibConfig) error {
|
|
prefix := strings.ToUpper(buildinfo.AppName) + "_HAMLIB_RIGS_"
|
|
for _, env := range os.Environ() {
|
|
attribute, value, _ := strings.Cut(env, "=")
|
|
if !strings.HasPrefix(attribute, prefix) {
|
|
continue
|
|
}
|
|
attribute = strings.TrimPrefix(attribute, prefix)
|
|
name, attribute, _ := strings.Cut(attribute, "_")
|
|
if *rigs == nil {
|
|
*rigs = make(map[string]cfg.HamlibConfig)
|
|
}
|
|
rig := (*rigs)[name]
|
|
switch attribute {
|
|
case "ADDRESS", "":
|
|
rig.Address = value
|
|
case "NETWORK":
|
|
rig.Network = value
|
|
case "VFO":
|
|
rig.VFO = value
|
|
default:
|
|
return fmt.Errorf("invalid attribute '%s' for rig '%s'", attribute, name)
|
|
}
|
|
(*rigs)[name] = rig
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ReadConfig(path string) (config cfg.Config, err error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return
|
|
}
|
|
err = json.Unmarshal(data, &config)
|
|
return
|
|
}
|
|
|
|
func WriteConfig(config cfg.Config, filePath string) error {
|
|
b, err := json.MarshalIndent(config, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Add trailing new-line
|
|
b = append(b, '\n')
|
|
|
|
// Ensure path dir is available
|
|
os.Mkdir(path.Dir(filePath), os.ModePerm|os.ModeDir)
|
|
|
|
return os.WriteFile(filePath, b, 0o600)
|
|
}
|