package config import ( "flag" "log" "os" "path" "strings" ) type Config struct { Bind string WifiInterface string HotspotInterface string WifiBackend string StationBackend string // "arp", "dhcp", or "hostapd" DHCPLeasesPath string ARPTablePath string SyslogEnabled bool SyslogPath string SyslogFilter []string SyslogSource string } // ConsolidateConfig fills an Options struct by reading configuration from // config files, environment, then command line. // // Should be called only one time. func ConsolidateConfig() (opts *Config, err error) { // Define defaults options opts = &Config{ Bind: ":8080", WifiInterface: "wlan0", HotspotInterface: "wlan1", DHCPLeasesPath: "/var/lib/dhcp/dhcpd.leases", ARPTablePath: "/proc/net/arp", SyslogEnabled: false, SyslogPath: "/var/log/messages", SyslogFilter: []string{"daemon.info wpa_supplicant:", "daemon.info iwd:", "daemon.info hostapd:"}, SyslogSource: "iwd", } declareFlags(opts) // Establish a list of possible configuration file locations configLocations := []string{ "repeater.conf", } if home, err := os.UserConfigDir(); err == nil { configLocations = append(configLocations, path.Join(home, "repeater", "repeater.conf")) } configLocations = append(configLocations, path.Join("etc", "repeater.conf")) // If config file exists, read configuration from it for _, filename := range configLocations { if _, e := os.Stat(filename); !os.IsNotExist(e) { log.Printf("Loading configuration from %s\n", filename) err = parseFile(opts, filename) if err != nil { return } break } } // Then, overwrite that by what is present in the environment err = parseEnvironmentVariables(opts) if err != nil { return } // Finaly, command line takes precedence err = parseCLI(opts) if err != nil { return } // Validate configuration if opts.WifiBackend != "iwd" && opts.WifiBackend != "wpasupplicant" { log.Fatalf("wifi-backend must be set to 'iwd' or 'wpasupplicant' (got: '%s')", opts.WifiBackend) } if opts.StationBackend != "arp" && opts.StationBackend != "dhcp" && opts.StationBackend != "hostapd" { log.Fatalf("station-backend must be set to 'arp', 'dhcp', or 'hostapd' (got: '%s')", opts.StationBackend) } return } // parseLine treats a config line and place the read value in the variable // declared to the corresponding flag. func parseLine(o *Config, line string) (err error) { fields := strings.SplitN(line, "=", 2) orig_key := strings.TrimSpace(fields[0]) value := strings.TrimSpace(fields[1]) key := strings.TrimPrefix(orig_key, "REPEATER_") key = strings.Replace(key, "_", "-", -1) key = strings.ToLower(key) err = flag.Set(key, value) return }