2022-04-24 01:18:26 +00:00
package config
import (
2022-07-07 15:00:46 +00:00
"bytes"
2022-04-24 01:18:26 +00:00
"fmt"
2022-07-07 15:00:46 +00:00
"io"
2022-04-24 01:18:26 +00:00
"os"
2022-07-07 15:00:46 +00:00
"path/filepath"
"runtime"
2022-07-10 07:14:45 +00:00
"runtime/debug"
2022-07-09 19:39:29 +00:00
"strings"
"sync"
2022-04-24 01:18:26 +00:00
"github.com/rs/zerolog"
2022-07-09 19:39:29 +00:00
"github.com/rs/zerolog/log"
2022-04-24 01:18:26 +00:00
"github.com/spf13/viper"
)
2022-07-07 15:00:46 +00:00
const (
// Title is the name of the application used throughout the configuration process.
Title = "tcp.ac"
)
2022-07-10 07:14:45 +00:00
var binInfo map [ string ] string
func init ( ) {
binInfo = make ( map [ string ] string )
info , ok := debug . ReadBuildInfo ( )
if ! ok {
return
}
for _ , v := range info . Settings {
binInfo [ v . Key ] = v . Value
}
var err error
home , err = os . UserHomeDir ( )
if err != nil {
println ( err . Error ( ) )
os . Exit ( 1 )
}
initDefaults ( )
}
2022-04-24 01:18:26 +00:00
2022-07-07 15:00:46 +00:00
var (
2022-07-08 20:23:20 +00:00
BaseURL , HTTPPort , HTTPBind , DBDir , LogDir ,
2022-07-18 10:41:29 +00:00
TermbinListen , UnixSocketPath , AdminKey string
2022-07-08 20:23:20 +00:00
UIDSize , DeleteKeySize , KVMaxKeySizeMB ,
KVMaxValueSizeMB int
UnixSocketPermissions uint32
UseUnixSocket bool
2022-07-18 11:22:26 +00:00
TrustedProxies [ ] string
2022-07-07 15:00:46 +00:00
)
2022-04-24 01:18:26 +00:00
2022-07-09 19:39:29 +00:00
var usage = fmt . Sprintf ( `
2022-07-10 07:14:45 +00:00
% s
2022-07-09 19:39:29 +00:00
brought to you by :
-- > tcp . direct <- -
2022-07-10 07:14:45 +00:00
-- config < file > Specify custom config file
-- nocolor Disable color and banner
-- genconfig Write default config to stdout and exit
-- version Show version info and exit
` , Title )
2022-07-07 15:00:46 +00:00
func printUsage ( ) {
println ( usage )
os . Exit ( 0 )
}
var (
2022-07-08 20:23:20 +00:00
forceDebug = false
forceTrace = false
genConfig = false
noColorForce = false
customconfig = false
home string
prefConfigLocation string
snek * viper . Viper
2022-07-07 15:00:46 +00:00
)
2022-04-24 01:18:26 +00:00
2022-07-07 15:00:46 +00:00
// TODO: should probably just make a proper CLI with flags or something
func argParse ( ) {
for i , arg := range os . Args {
switch arg {
case "-h" :
printUsage ( )
case "--genconfig" :
2022-07-08 20:23:20 +00:00
genConfig = true
2022-07-07 15:00:46 +00:00
case "--debug" , "-v" :
forceDebug = true
case "--trace" , "-vv" :
forceTrace = true
case "--nocolor" :
noColorForce = true
2022-07-10 07:14:45 +00:00
case "--version" :
PrintBanner ( )
os . Exit ( 0 )
2022-07-07 15:00:46 +00:00
case "-c" , "--config" :
if len ( os . Args ) <= i - 1 {
panic ( "syntax error! expected file after -c" )
}
default :
continue
}
2022-04-24 01:18:26 +00:00
}
2022-07-07 15:00:46 +00:00
}
2022-04-24 01:18:26 +00:00
2022-07-07 15:00:46 +00:00
// exported generic vars
var (
// Trace is the value of our trace (extra verbose) on/off toggle as per the current configuration.
Trace bool
// Debug is the value of our debug (verbose) on/off toggle as per the current configuration.
Debug bool
// Filename returns the current location of our toml config file.
Filename string
)
func writeConfig ( ) {
var err error
//goland:noinspection GoBoolExpressions
if runtime . GOOS == "windows" {
2022-07-09 19:39:29 +00:00
newconfig := Title
2022-07-07 15:00:46 +00:00
snek . SetConfigName ( newconfig )
if err = snek . MergeInConfig ( ) ; err != nil {
if err = snek . SafeWriteConfigAs ( newconfig + ".toml" ) ; err != nil {
fmt . Println ( err . Error ( ) )
os . Exit ( 1 )
}
}
2022-07-09 19:39:29 +00:00
Filename = newconfig + ".toml"
2022-07-07 15:00:46 +00:00
return
}
if _ , err := os . Stat ( prefConfigLocation ) ; os . IsNotExist ( err ) {
2022-07-09 19:39:29 +00:00
if err = os . MkdirAll ( prefConfigLocation , 0 o740 ) ; err != nil {
2022-07-07 15:00:46 +00:00
println ( "error writing new config: " + err . Error ( ) )
os . Exit ( 1 )
}
}
2022-07-09 19:39:29 +00:00
newconfig := prefConfigLocation + "config.toml"
2022-07-07 15:00:46 +00:00
if err = snek . SafeWriteConfigAs ( newconfig ) ; err != nil {
2022-07-09 19:39:29 +00:00
log . Fatal ( ) . Caller ( ) . Err ( err ) . Str ( "target" , newconfig ) . Msg ( "failed to write new configuration file" )
2022-07-07 15:00:46 +00:00
}
Filename = newconfig
}
2022-07-09 19:39:29 +00:00
func init ( ) {
2022-07-10 07:14:45 +00:00
2022-07-09 19:39:29 +00:00
}
var once = & sync . Once { }
func substantiateLogger ( ) {
once . Do ( func ( ) {
consoleWriter := zerolog . ConsoleWriter { Out : os . Stdout }
lf , err := os . OpenFile ( LogDir + "tcpac.log" , os . O_RDWR | os . O_CREATE | os . O_APPEND , 0 o666 )
if err != nil {
log . Fatal ( ) . Str ( "config.LogDir" , LogDir ) . Err ( err ) . Msg ( "Error opening log file!" )
}
multi := zerolog . MultiLevelWriter ( consoleWriter , lf )
log . Logger = zerolog . New ( multi ) . With ( ) . Timestamp ( ) . Logger ( )
} )
}
2022-07-07 15:00:46 +00:00
// Init will initialize our toml configuration engine and define our default configuration values which can be written to a new configuration file if desired
func Init ( ) {
2022-07-08 20:23:20 +00:00
argParse ( )
2022-07-09 19:39:29 +00:00
zerolog . TimeFieldFormat = zerolog . TimeFormatUnix
consoleWriter := zerolog . ConsoleWriter { Out : os . Stdout }
log . Logger = log . Output ( consoleWriter ) . With ( ) . Timestamp ( ) . Logger ( )
prefConfigLocation = home + "/.config/" + Title + "/"
2022-07-08 20:23:20 +00:00
snek = viper . New ( )
if genConfig {
setDefaults ( )
os . Exit ( 0 )
}
2022-07-07 15:00:46 +00:00
snek . SetConfigType ( "toml" )
snek . SetConfigName ( "config" )
if customconfig {
associateExportedVariables ( )
2022-07-09 19:39:29 +00:00
substantiateLogger ( )
2022-07-07 15:00:46 +00:00
return
2022-04-24 01:18:26 +00:00
}
2022-07-07 15:00:46 +00:00
setDefaults ( )
2022-04-24 01:18:26 +00:00
2022-07-07 15:00:46 +00:00
for _ , loc := range getConfigPaths ( ) {
snek . AddConfigPath ( loc )
}
2022-04-24 01:18:26 +00:00
2022-07-08 20:23:20 +00:00
if err := snek . MergeInConfig ( ) ; err != nil {
2022-07-09 19:39:29 +00:00
substantiateLogger ( )
log . Warn ( ) . Err ( err ) . Msg ( "failed to read configuration file" )
2022-07-07 15:00:46 +00:00
writeConfig ( )
}
if len ( Filename ) < 1 {
Filename = snek . ConfigFileUsed ( )
}
2022-07-09 19:39:29 +00:00
substantiateLogger ( )
2022-07-07 15:00:46 +00:00
associateExportedVariables ( )
}
func getConfigPaths ( ) ( paths [ ] string ) {
paths = append ( paths , "./" )
//goland:noinspection GoBoolExpressions
if runtime . GOOS != "windows" {
paths = append ( paths ,
prefConfigLocation , "/etc/" + Title + "/" , "../" , "../../" )
}
return
}
2022-07-18 10:41:29 +00:00
// TODO: use this?
2022-07-07 15:00:46 +00:00
func loadCustomConfig ( path string ) {
/* #nosec */
2022-07-08 20:23:20 +00:00
f , err := os . Open ( path )
if err != nil {
2022-07-07 15:00:46 +00:00
println ( "Error opening specified config file: " + path )
println ( err . Error ( ) )
os . Exit ( 1 )
}
Filename , _ = filepath . Abs ( path )
if len ( Filename ) < 1 {
Filename = path
}
defer func ( f * os . File ) {
fcerr := f . Close ( )
if fcerr != nil {
fmt . Println ( "failed to close file handler for config file: " , fcerr . Error ( ) )
}
} ( f )
buf , err1 := io . ReadAll ( f )
err2 := snek . ReadConfig ( bytes . NewBuffer ( buf ) )
switch {
case err1 != nil :
2022-07-09 19:39:29 +00:00
log . Fatal ( ) . Err ( err1 ) . Msg ( "config file read fatal error during i/o" )
2022-07-07 15:00:46 +00:00
case err2 != nil :
2022-07-09 19:39:29 +00:00
log . Fatal ( ) . Err ( err2 ) . Msg ( "config file read fatal error during parsing" )
2022-07-07 15:00:46 +00:00
default :
break
}
customconfig = true
}
func processOpts ( ) {
// string options and their exported variables
stringOpt := map [ string ] * string {
"http.bind_addr" : & HTTPBind ,
"http.bind_port" : & HTTPPort ,
2022-07-08 20:23:20 +00:00
"http.unix_socket_path" : & UnixSocketPath ,
2022-07-09 19:39:29 +00:00
"data.directory" : & DBDir ,
2022-07-07 15:00:46 +00:00
"logger.directory" : & LogDir ,
2022-07-08 20:23:20 +00:00
"other.termbin_listen" : & TermbinListen ,
2022-07-09 19:39:29 +00:00
"other.base_url" : & BaseURL ,
2022-07-18 10:41:29 +00:00
"admin.key" : & AdminKey ,
2022-07-09 19:39:29 +00:00
}
2022-07-18 11:22:26 +00:00
stringSliceOpt := map [ string ] * [ ] string {
"http.trusted_proxies" : & TrustedProxies ,
}
2022-07-09 19:39:29 +00:00
if ! strings . HasSuffix ( BaseURL , "/" ) {
BaseURL += "/"
2022-07-07 15:00:46 +00:00
}
2022-07-08 20:23:20 +00:00
2022-07-07 15:00:46 +00:00
// bool options and their exported variables
boolOpt := map [ string ] * bool {
2022-07-08 20:23:20 +00:00
"http.use_unix_socket" : & UseUnixSocket ,
"logger.debug" : & Debug ,
"logger.trace" : & Trace ,
"logger.nocolor" : & noColorForce ,
2022-07-07 15:00:46 +00:00
}
2022-07-08 20:23:20 +00:00
2022-07-07 15:00:46 +00:00
// integer options and their exported variables
intOpt := map [ string ] * int {
2022-07-09 19:39:29 +00:00
"data.max_key_size" : & KVMaxKeySizeMB ,
"data.max_value_size" : & KVMaxValueSizeMB ,
"other.uid_size" : & UIDSize ,
"other.delete_key_size" : & DeleteKeySize ,
2022-07-08 20:23:20 +00:00
}
uint32Opt := map [ string ] * uint32 {
"http.unix_socket_permissions" : & UnixSocketPermissions ,
2022-07-07 15:00:46 +00:00
}
for key , opt := range stringOpt {
* opt = snek . GetString ( key )
}
for key , opt := range boolOpt {
* opt = snek . GetBool ( key )
}
for key , opt := range intOpt {
* opt = snek . GetInt ( key )
}
2022-07-08 20:23:20 +00:00
for key , opt := range uint32Opt {
* opt = snek . GetUint32 ( key )
}
2022-07-18 11:22:26 +00:00
for key , opt := range stringSliceOpt {
* opt = snek . GetStringSlice ( key )
}
2022-07-07 15:00:46 +00:00
}
func associateExportedVariables ( ) {
processOpts ( )
2022-07-29 06:53:12 +00:00
zerolog . SetGlobalLevel ( zerolog . InfoLevel )
2022-07-07 15:00:46 +00:00
// We set exported variables here so that it tracks when accessed from other packages.
if Debug || forceDebug {
zerolog . SetGlobalLevel ( zerolog . DebugLevel )
Debug = true
}
if Trace || forceTrace {
zerolog . SetGlobalLevel ( zerolog . TraceLevel )
Trace = true
}
2022-04-24 01:18:26 +00:00
}