290 lines
8.7 KiB
Go
290 lines
8.7 KiB
Go
package gotop
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"embed"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/shibukawa/configdir"
|
|
"github.com/xxxserxxx/gotop/v4/colorschemes"
|
|
"github.com/xxxserxxx/gotop/v4/widgets"
|
|
"github.com/xxxserxxx/lingo/v2"
|
|
)
|
|
|
|
// FIXME github action uses old(er) Go version that doesn't have embed
|
|
//go:embed "dicts/*.toml"
|
|
var Dicts embed.FS
|
|
|
|
// CONFFILE is the name of the default config file
|
|
const CONFFILE = "gotop.conf"
|
|
|
|
type Config struct {
|
|
ConfigDir configdir.ConfigDir
|
|
GraphHorizontalScale int
|
|
HelpVisible bool
|
|
Colorscheme colorschemes.Colorscheme
|
|
UpdateInterval time.Duration
|
|
AverageLoad bool
|
|
PercpuLoad bool
|
|
Statusbar bool
|
|
TempScale widgets.TempScale
|
|
NetInterface string
|
|
Layout string
|
|
MaxLogSize int64
|
|
ExportPort string
|
|
Mbps bool
|
|
Temps []string
|
|
Test bool
|
|
ExtensionVars map[string]string
|
|
ConfigFile string
|
|
Tr lingo.Translations
|
|
Nvidia bool
|
|
NvidiaRefresh time.Duration
|
|
}
|
|
|
|
// FIXME parsing can't handle blank lines
|
|
func NewConfig() Config {
|
|
cd := configdir.New("", "gotop")
|
|
cd.LocalPath, _ = filepath.Abs(".")
|
|
conf := Config{
|
|
ConfigDir: cd,
|
|
GraphHorizontalScale: 7,
|
|
HelpVisible: false,
|
|
UpdateInterval: time.Second,
|
|
AverageLoad: false,
|
|
PercpuLoad: true,
|
|
TempScale: widgets.Celsius,
|
|
Statusbar: false,
|
|
NetInterface: widgets.NetInterfaceAll,
|
|
MaxLogSize: 5000000,
|
|
Layout: "default",
|
|
ExtensionVars: make(map[string]string),
|
|
}
|
|
conf.Colorscheme, _ = colorschemes.FromName(conf.ConfigDir, "default")
|
|
folder := conf.ConfigDir.QueryFolderContainsFile(CONFFILE)
|
|
if folder != nil {
|
|
conf.ConfigFile = filepath.Join(folder.Path, CONFFILE)
|
|
}
|
|
return conf
|
|
}
|
|
|
|
func (conf *Config) Load() error {
|
|
var in []byte
|
|
if conf.ConfigFile == "" {
|
|
return nil
|
|
}
|
|
var err error
|
|
if _, err = os.Stat(conf.ConfigFile); os.IsNotExist(err) {
|
|
// Check for the file in the usual suspects
|
|
folder := conf.ConfigDir.QueryFolderContainsFile(conf.ConfigFile)
|
|
if folder == nil {
|
|
return nil
|
|
}
|
|
conf.ConfigFile = filepath.Join(folder.Path, conf.ConfigFile)
|
|
}
|
|
if in, err = ioutil.ReadFile(conf.ConfigFile); err != nil {
|
|
return err
|
|
}
|
|
return load(bytes.NewReader(in), conf)
|
|
}
|
|
|
|
func load(in io.Reader, conf *Config) error {
|
|
r := bufio.NewScanner(in)
|
|
var lineNo int
|
|
for r.Scan() {
|
|
l := strings.TrimSpace(r.Text())
|
|
if l[0] == '#' {
|
|
continue
|
|
}
|
|
kv := strings.Split(l, "=")
|
|
if len(kv) != 2 {
|
|
return fmt.Errorf(conf.Tr.Value("config.err.configsyntax", l))
|
|
}
|
|
key := strings.ToLower(kv[0])
|
|
ln := strconv.Itoa(lineNo)
|
|
switch key {
|
|
default:
|
|
conf.ExtensionVars[key] = kv[1]
|
|
case "configdir", "logdir", "logfile":
|
|
log.Printf(conf.Tr.Value("config.err.deprecation", ln, key, kv[1]))
|
|
case graphhorizontalscale:
|
|
iv, err := strconv.Atoi(kv[1])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
conf.GraphHorizontalScale = iv
|
|
case helpvisible:
|
|
bv, err := strconv.ParseBool(kv[1])
|
|
if err != nil {
|
|
return fmt.Errorf(conf.Tr.Value("config.err.line", ln, err.Error()))
|
|
}
|
|
conf.HelpVisible = bv
|
|
case colorscheme:
|
|
cs, err := colorschemes.FromName(conf.ConfigDir, kv[1])
|
|
if err != nil {
|
|
return fmt.Errorf(conf.Tr.Value("config.err.line", ln, err.Error()))
|
|
}
|
|
conf.Colorscheme = cs
|
|
case updateinterval:
|
|
iv, err := strconv.Atoi(kv[1])
|
|
if err != nil {
|
|
return fmt.Errorf(conf.Tr.Value("config.err.line", ln, err.Error()))
|
|
}
|
|
conf.UpdateInterval = time.Duration(iv)
|
|
case averagecpu:
|
|
bv, err := strconv.ParseBool(kv[1])
|
|
if err != nil {
|
|
return fmt.Errorf(conf.Tr.Value("config.err.line", ln, err.Error()))
|
|
}
|
|
conf.AverageLoad = bv
|
|
case percpuload:
|
|
bv, err := strconv.ParseBool(kv[1])
|
|
if err != nil {
|
|
return fmt.Errorf(conf.Tr.Value("config.err.line", ln, err.Error()))
|
|
}
|
|
conf.PercpuLoad = bv
|
|
case tempscale:
|
|
switch kv[1] {
|
|
case "C":
|
|
conf.TempScale = 'C'
|
|
case "F":
|
|
conf.TempScale = 'F'
|
|
default:
|
|
conf.TempScale = 'C'
|
|
return fmt.Errorf(conf.Tr.Value("config.err.tempscale", kv[1]))
|
|
}
|
|
case statusbar:
|
|
bv, err := strconv.ParseBool(kv[1])
|
|
if err != nil {
|
|
return fmt.Errorf(conf.Tr.Value("config.err.line", ln, err.Error()))
|
|
}
|
|
conf.Statusbar = bv
|
|
case netinterface:
|
|
conf.NetInterface = kv[1]
|
|
case layout:
|
|
conf.Layout = kv[1]
|
|
case maxlogsize:
|
|
iv, err := strconv.Atoi(kv[1])
|
|
if err != nil {
|
|
return fmt.Errorf(conf.Tr.Value("config.err.line", ln, err.Error()))
|
|
}
|
|
conf.MaxLogSize = int64(iv)
|
|
case export:
|
|
conf.ExportPort = kv[1]
|
|
case mbps:
|
|
conf.Mbps = true
|
|
case temperatures:
|
|
conf.Temps = strings.Split(kv[1], ",")
|
|
case nvidia:
|
|
nv, err := strconv.ParseBool(kv[1])
|
|
if err != nil {
|
|
return fmt.Errorf(conf.Tr.Value("config.err.line", ln, err.Error()))
|
|
}
|
|
conf.Nvidia = nv
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Write serializes the configuration to a file.
|
|
// The configuration written is based on the loaded configuration, plus any
|
|
// command-line changes, so it can be used to update an existing configuration
|
|
// file. The file will be written to the specificed `--config` argument file,
|
|
// if one is set; otherwise, it'll create one in the user's config directory.
|
|
func (conf *Config) Write() (string, error) {
|
|
var dir *configdir.Config
|
|
var file string = CONFFILE
|
|
if conf.ConfigFile == "" {
|
|
ds := conf.ConfigDir.QueryFolders(configdir.Global)
|
|
if len(ds) == 0 {
|
|
ds = conf.ConfigDir.QueryFolders(configdir.Local)
|
|
if len(ds) == 0 {
|
|
return "", fmt.Errorf("error locating config folders")
|
|
}
|
|
}
|
|
ds[0].CreateParentDir(CONFFILE)
|
|
dir = ds[0]
|
|
} else {
|
|
dir = &configdir.Config{}
|
|
dir.Path = filepath.Dir(conf.ConfigFile)
|
|
file = filepath.Base(conf.ConfigFile)
|
|
}
|
|
marshalled := marshal(conf)
|
|
err := dir.WriteFile(file, marshalled)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return filepath.Join(dir.Path, file), nil
|
|
}
|
|
|
|
func marshal(c *Config) []byte {
|
|
buff := bytes.NewBuffer(nil)
|
|
fmt.Fprintln(buff, "# Scale graphs to this level; 7 is the default, 2 is zoomed out.")
|
|
fmt.Fprintf(buff, "%s=%d\n", graphhorizontalscale, c.GraphHorizontalScale)
|
|
fmt.Fprintln(buff, "# If true, start the UI with the help visible")
|
|
fmt.Fprintf(buff, "%s=%t\n", helpvisible, c.HelpVisible)
|
|
fmt.Fprintln(buff, "# The color scheme to use. See `--list colorschemes`")
|
|
fmt.Fprintf(buff, "%s=%s\n", colorscheme, c.Colorscheme.Name)
|
|
fmt.Fprintln(buff, "# How frequently to update the UI, in nanoseconds")
|
|
fmt.Fprintf(buff, "%s=%d\n", updateinterval, c.UpdateInterval)
|
|
fmt.Fprintln(buff, "# If true, show the average CPU load")
|
|
fmt.Fprintf(buff, "%s=%t\n", averagecpu, c.AverageLoad)
|
|
fmt.Fprintln(buff, "# If true, show load per CPU")
|
|
fmt.Fprintf(buff, "%s=%t\n", percpuload, c.PercpuLoad)
|
|
fmt.Fprintln(buff, "# Temperature units. C for Celsius, F for Fahrenheit")
|
|
fmt.Fprintf(buff, "%s=%c\n", tempscale, c.TempScale)
|
|
fmt.Fprintln(buff, "# If true, display a status bar")
|
|
fmt.Fprintf(buff, "%s=%t\n", statusbar, c.Statusbar)
|
|
fmt.Fprintln(buff, "# The network interface to monitor")
|
|
fmt.Fprintf(buff, "%s=%s\n", netinterface, c.NetInterface)
|
|
fmt.Fprintln(buff, "# A layout name. See `--list layouts`")
|
|
fmt.Fprintf(buff, "%s=%s\n", layout, c.Layout)
|
|
fmt.Fprintln(buff, "# The maximum log file size, in bytes")
|
|
fmt.Fprintf(buff, "%s=%d\n", maxlogsize, c.MaxLogSize)
|
|
fmt.Fprintln(buff, "# If set, export data as Promethius metrics on the interface:port.\n# E.g., `:8080` (colon is required, interface is not)")
|
|
if c.ExportPort == "" {
|
|
fmt.Fprint(buff, "#")
|
|
}
|
|
fmt.Fprintf(buff, "%s=%s\n", export, c.ExportPort)
|
|
fmt.Fprintln(buff, "# Display network IO in mpbs if true")
|
|
fmt.Fprintf(buff, "%s=%t\n", mbps, c.Mbps)
|
|
fmt.Fprintln(buff, "# A list of enabled temp sensors. See `--list devices`")
|
|
if len(c.Temps) == 0 {
|
|
fmt.Fprint(buff, "#")
|
|
}
|
|
fmt.Fprintf(buff, "%s=%s\n", temperatures, strings.Join(c.Temps, ","))
|
|
fmt.Fprintln(buff, "# Enable NVidia GPU metrics.")
|
|
fmt.Fprintf(buff, "%s=%t\n", nvidia, c.Nvidia)
|
|
fmt.Fprintln(buff, "# To configure the NVidia refresh rate, set a duration:")
|
|
fmt.Fprintln(buff, "#nvidiarefresh=30s")
|
|
return buff.Bytes()
|
|
}
|
|
|
|
const (
|
|
graphhorizontalscale = "graphhorizontalscale"
|
|
helpvisible = "helpvisible"
|
|
colorscheme = "colorscheme"
|
|
updateinterval = "updateinterval"
|
|
averagecpu = "averagecpu"
|
|
percpuload = "percpuload"
|
|
tempscale = "tempscale"
|
|
statusbar = "statusbar"
|
|
netinterface = "netinterface"
|
|
layout = "layout"
|
|
maxlogsize = "maxlogsize"
|
|
export = "metricsexportport"
|
|
mbps = "mbps"
|
|
temperatures = "temperatures"
|
|
nvidia = "nvidia"
|
|
)
|