summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/release.yml8
-rw-r--r--.gitignore1
-rw-r--r--CHANGELOG.md20
-rw-r--r--README.md14
-rw-r--r--assets/screenshots/fourby.pngbin0 -> 186287 bytes
-rw-r--r--cmd/gotop/main.go13
-rw-r--r--config.go13
-rw-r--r--devices/nvidia.go179
-rw-r--r--devices/remote.go271
-rw-r--r--dicts/de_DE.toml130
-rw-r--r--dicts/en_US.toml4
-rw-r--r--dicts/eo.toml4
-rw-r--r--dicts/fr.toml185
-rw-r--r--dicts/ru_RU.toml177
-rw-r--r--dicts/tt_TT.toml4
-rw-r--r--dicts/zh_CN.toml4
-rw-r--r--docs/extensions.md12
-rw-r--r--docs/nvidia-extension.md17
-rw-r--r--docs/releasing.md29
-rw-r--r--docs/remote-monitoring.md62
-rw-r--r--widgets/help.go42
21 files changed, 1050 insertions, 139 deletions
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index caa2854..6c15c8d 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -14,14 +14,6 @@ jobs:
run: echo "##[set-output name=tag;]$(echo ${GITHUB_REF##*/})"
id: tag_name
- - name: Trigger extensions build
- uses: peter-evans/repository-dispatch@v1
- with:
- token: ${{ secrets.REPO_ACCESS_TOKEN }}
- repository: xxxserxxx/gotop-builder
- event-type: my-release
- client-payload: '{"tag": "${{ steps.tag_name.outputs.tag }}"}'
-
- name: Update Homebrew recipe
uses: peter-evans/repository-dispatch@v1
with:
diff --git a/.gitignore b/.gitignore
index 9588e08..f08e462 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,4 @@ build/gotop_*
build.log
tmp/
+dist/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 99bd79b..56f8bf6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
> - **Fixed**: for any bug fixes.
> - **Security**: in case of vulnerabilities.
+## [4.1.2]
+
+### Added
+
+- French and Russion translations (thank you @lourkeur and @talentlessguy!)
+- nvidia support merged in from extension
+- remote support merged in from extension
+
+### Changed
+
+- Upgrade to Go 1.16. This eliminates go:generate for the language files, which
+ means gotop no longer builds with Go < 1.16.
+
+### Fixed
+
+- German translation corrected (thanks @plgruener!)
+- Extra spaces in help text (#167)
+- Crash with German translation (#166)
+- Bad error message for missing layouts (#164)
+
## [4.1.1] 2021-02-03
### Added
diff --git a/README.md b/README.md
index ccd7f21..6abde81 100644
--- a/README.md
+++ b/README.md
@@ -38,20 +38,14 @@ If you install gotop by hand, or you download or create new layouts or colorsche
- **Prebuild binaries with extensions**:
- [NVidia GPU support](https://github.com/xxxserxxx/gotop-nvidia/releases)
- [Remote gotop support](https://github.com/xxxserxxx/gotop-remote/releases)
-- **Source**: This requires Go >= 1.16. `go get -u github.com/xxxserxxx/gotop/cmd/gotop`
+- **Source**: This requires Go >= 1.16. `go get -u github.com/xxxserxxx/gotop/v4/cmd/gotop`
### Extension builds
-An evolving mechanism in gotop are extensions. This is designed to allow gotop to support feature sets that are not universally needed without blowing up the application for average users with unused features. Examples are support for specific hardware sets like video cards, or things that are just obviously not a core objective of the application, like remote server monitoring.
+Extensions have proven problematic; go plugins are not usable in real-world cases, and the solution I had running for a while was hacky, at best. Consequently, extensions have been moved into the main code base for now.
-The path to these extensions is a tool called [gotop-builder](https://github.com/xxxserxxx/gotop-builder). It is easy to use and depends only on having Go installed. You can read more about it on the project page, where you can also find binaries for Linux that have *all* extensions built in. If you want less than an all-inclusive build, or one for a different OS/architecture, you can use gotop-builder itself to create your own.
-
-There are currently two extensions:
-
-- Support for [NVidia GPUs](https://github.com/xxxserxxx/gotop-nvidia), which add GPU usage, memory, and temperature data to the respective widgets
-- Support for [remote devices](https://github.com/xxxserxxx/gotop-remote), which allows running gotop on a remote machine and seeing the sensors from that as if they were local sensors.
-
-There are builds for those binaries for Linux in each of the repositories.
+- nvidia support: use the `--nvidia` flag to enable. You must have the `nvidia- smi` package installed, and gotop must be able to find the `nvidia-smi` executable, for this to work.
+- remote: allows gotop to pull sensor data from applications exporting Prometheus metrics, including remote gotop instances themselves.
### Console Users
diff --git a/assets/screenshots/fourby.png b/assets/screenshots/fourby.png
new file mode 100644
index 0000000..12c99ba
--- /dev/null
+++ b/assets/screenshots/fourby.png
Binary files differ
diff --git a/cmd/gotop/main.go b/cmd/gotop/main.go
index 4a8416f..7f08626 100644
--- a/cmd/gotop/main.go
+++ b/cmd/gotop/main.go
@@ -77,6 +77,7 @@ func parseArgs() error {
opflag.BoolVarP(&conf.Mbps, "mbps", "", conf.Mbps, tr.Value("args.mbps"))
opflag.BoolVar(&conf.Test, "test", conf.Test, tr.Value("args.test"))
opflag.StringP("", "C", "", tr.Value("args.conffile"))
+ opflag.BoolVarP(&conf.Nvidia, "nvidia", "", conf.Nvidia, "Enable NVidia GPU support")
list := opflag.String("list", "", tr.Value("args.list"))
wc := opflag.Bool("write-config", false, tr.Value("args.write"))
opflag.SortFlags = false
@@ -147,6 +148,9 @@ func parseArgs() error {
}
os.Exit(0)
}
+ if conf.Nvidia {
+ conf.ExtensionVars["nvidia"] = "true"
+ }
if *wc {
path, err := conf.Write()
if err != nil {
@@ -413,12 +417,9 @@ func run() int {
}
defer logfile.Close()
- errs := devices.Startup(conf.ExtensionVars)
- if len(errs) > 0 {
- for _, err := range errs {
- stderrLogger.Print(err)
- }
- return 1
+ // device initialization errors do not stop execution
+ for _, err := range devices.Startup(conf.ExtensionVars) {
+ stderrLogger.Print(err)
}
lstream, err := getLayout(conf)
diff --git a/config.go b/config.go
index 68ec523..23754e4 100644
--- a/config.go
+++ b/config.go
@@ -49,6 +49,8 @@ type Config struct {
ExtensionVars map[string]string
ConfigFile string
Tr lingo.Translations
+ Nvidia bool
+ NvidiaRefresh time.Duration
}
func NewConfig() Config {
@@ -183,6 +185,12 @@ func load(in io.Reader, conf *Config) error {
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
}
}
@@ -256,6 +264,10 @@ func marshal(c *Config) []byte {
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()
}
@@ -274,4 +286,5 @@ const (
export = "metricsexportport"
mbps = "mbps"
temperatures = "temperatures"
+ nvidia = "nvidia"
)
diff --git a/devices/nvidia.go b/devices/nvidia.go
new file mode 100644
index 0000000..f9f421d
--- /dev/null
+++ b/devices/nvidia.go
@@ -0,0 +1,179 @@
+package devices
+
+import (
+ "bytes"
+ "encoding/csv"
+ "errors"
+ "fmt"
+ "os/exec"
+ "strconv"
+ "sync"
+ "time"
+)
+
+// Set up variables and register this plug-in with the main code.
+// The functions Register*(f) tell gotop which of these plugin functions to
+// call to update data; the RegisterStartup() function sets the function
+// that gotop will call when everything else has been done and the plugin
+// should start collecting data.
+//
+// In this plugin, one call to the nvidia program returns *all* the data
+// we're looking for, but gotop will call each update function during each
+// cycle. This means that the nvidia program would be called 3 (or more)
+// times per update, which isn't very efficient. Therefore, we make this
+// code more complex to run a job in the background that runs the nvidia
+// tool periodically and puts the results into hashes; the update functions
+// then just sync data from those hashes into the return data.
+func init() {
+ RegisterStartup(startNVidia)
+}
+
+// updateNvidiaTemp copies data from the local _temps cache into the passed-in
+// return-value map. It is called once per cycle by gotop.
+func updateNvidiaTemp(temps map[string]int) map[string]error {
+ nvidiaLock.Lock()
+ defer nvidiaLock.Unlock()
+ for k, v := range _temps {
+ temps[k] = v
+ }
+ return _errors
+}
+
+// updateNvidiaMem copies data from the local _mems cache into the passed-in
+// return-value map. It is called once per cycle by gotop.
+func updateNvidiaMem(mems map[string]MemoryInfo) map[string]error {
+ nvidiaLock.Lock()
+ defer nvidiaLock.Unlock()
+ for k, v := range _mems {
+ mems[k] = v
+ }
+ return _errors
+}
+
+// updateNvidiaUsage copies data from the local _cpus cache into the passed-in
+// return-value map. It is called once per cycle by gotop.
+func updateNvidiaUsage(cpus map[string]int, _ bool) map[string]error {
+ nvidiaLock.Lock()
+ defer nvidiaLock.Unlock()
+ for k, v := range _cpus {
+ cpus[k] = v
+ }
+ return _errors
+}
+
+// startNVidia is called once by gotop, and forks a thread to call the nvidia
+// tool periodically and update the cached cpu, memory, and temperature
+// values that are used by the update*() functions to return data to gotop.
+//
+// The vars argument contains command-line arguments to allow the plugin
+// to change runtime options; the only option currently supported is the
+// `nvidia-refresh` arg, which is expected to be a time.Duration value and
+// sets how frequently the nvidia tool is called to refresh the date.
+func startNVidia(vars map[string]string) error {
+ if vars["nvidia"] != "true" {
+ return nil
+ }
+ _, err := exec.Command("nvidia-smi", "-L").Output()
+ if err != nil {
+ return errors.New(fmt.Sprintf("NVidia GPU error: %s", err))
+ }
+ _errors = make(map[string]error)
+ _temps = make(map[string]int)
+ _mems = make(map[string]MemoryInfo)
+ _cpus = make(map[string]int)
+ _errors = make(map[string]error)
+ RegisterTemp(updateNvidiaTemp)
+ RegisterMem(updateNvidiaMem)
+ RegisterCPU(updateNvidiaUsage)
+
+ nvidiaLock = sync.Mutex{}
+ // Get the refresh period from the passed-in command-line/config
+ // file options
+ refresh := time.Second
+ if v, ok := vars["nvidia-refresh"]; ok {
+ if refresh, err = time.ParseDuration(v); err != nil {
+ return err
+ }
+ }
+ // update once to populate the device names, for the widgets.
+ updateNvidia()
+ // Fork off a long-running job to call the nvidia tool periodically,
+ // parse out the values, and put them in the cache.
+ go func() {
+ timer := time.Tick(refresh)
+ for range timer {
+ updateNvidia()
+ }
+ }()
+ return nil
+}
+
+// Caches for the output from the nvidia tool; the update() functions pull
+// from these and return the values to gotop when requested.
+var (
+ _temps map[string]int
+ _mems map[string]MemoryInfo
+ _cpus map[string]int
+ // A cache of errors generated by the background job running the nvidia tool;
+ // these errors are returned to gotop when it calls the update() functions.
+ _errors map[string]error
+)
+
+var nvidiaLock sync.Mutex
+
+// updateNvidia calls the nvidia tool, parses the output, and caches the results
+// in the various _* maps. The metric data parsed is: name, index,
+// temperature.gpu, utilization.gpu, utilization.memory, memory.total,
+// memory.free, memory.used
+//
+// If this function encounters an error calling `nvidia-smi`, it caches the
+// error and returns immediately. We expect exec errors only when the tool
+// isn't available, or when it fails for some reason; no exec error cases
+// are recoverable. This does **not** stop the cache job; that will continue
+// to run and continue to call updateNvidia().
+func updateNvidia() {
+ bs, err := exec.Command(
+ "nvidia-smi",
+ "--query-gpu=name,index,temperature.gpu,utilization.gpu,memory.total,memory.used",
+ "--format=csv,noheader,nounits").Output()
+ if err != nil {
+ _errors["nvidia"] = err
+ //bs = []byte("GeForce GTX 1080 Ti, 0, 31, 9, 11175, 206")
+ return
+ }
+ csvReader := csv.NewReader(bytes.NewReader(bs))
+ csvReader.TrimLeadingSpace = true
+ records, err := csvReader.ReadAll()
+ if err != nil {
+ _errors["nvidia"] = err
+ return
+ }
+
+ // Ensure we're not trying to modify the caches while they're being read by the update() functions.
+ nvidiaLock.Lock()
+ defer nvidiaLock.Unlock()
+ // Errors during parsing are recorded, but do not stop parsing.
+ for _, row := range records {
+ // The name of the devices is the nvidia-smi "<name>.<index>"
+ name := row[0] + "." + row[1]
+ if _temps[name], err = strconv.Atoi(row[2]); err != nil {
+ _errors[name] = err
+ }
+ if _cpus[name], err = strconv.Atoi(row[3]); err != nil {
+ _errors[name] = err
+ }
+ t, err := strconv.Atoi(row[4])
+ if err != nil {
+ _errors[name] = err
+ }
+ u, err := strconv.Atoi(row[5])
+ if err != nil {
+ _errors[name] = err
+ }
+ _mems[name] = MemoryInfo{
+ Total: 1048576 * uint64(t),
+ Used: 1048576 * uint64(u),
+ UsedPercent: (float64(u) / float64(t)) * 100.0,
+ }
+ }
+}
diff --git a/devices/remote.go b/devices/remote.go
new file mode 100644
index 0000000..a86f3ee
--- /dev/null
+++ b/devices/remote.go
@@ -0,0 +1,271 @@
+package devices
+
+import (
+ "bufio"
+ "log"
+ "net/http"
+ "net/url"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/xxxserxxx/opflag"
+)
+
+var name string
+var remote_url string
+var sleep time.Duration
+var remoteLock sync.Mutex
+
+// FIXME Widgets don't align values
+// TODO remote network & disk aren't reported
+// TODO network resiliency; I believe it currently crashes gotop when the network goes down
+// TODO Replace custom decoder with https://github.com/prometheus/common/blob/master/expfmt/decode.go
+// TODO MQTT / Stomp / MsgPack
+func init() {
+ opflag.StringVarP(&name, "remote-name", "", "", "Remote: name of remote gotop")
+ opflag.StringVarP(&remote_url, "remote-url", "", "", "Remote: URL of remote gotop")
+ opflag.DurationVarP(&sleep, "remote-refresh", "", 0, "Remote: Frequency to refresh data, in seconds")
+
+ RegisterStartup(startup)
+}
+
+type Remote struct {
+ url string
+ refresh time.Duration
+}
+
+func startup(vars map[string]string) error {
+ // Don't set anything up if there's nothing to do
+ if name == "" || remote_url == "" {
+ return nil
+ }
+ _cpuData = make(map[string]int)
+ _tempData = make(map[string]int)
+ _netData = make(map[string]float64)
+ _diskData = make(map[string]float64)
+ _memData = make(map[string]MemoryInfo)
+
+ remoteLock = sync.Mutex{}
+ remotes := parseConfig(vars)
+ if remote_url != "" {
+ r := Remote{
+ url: remote_url,
+ refresh: 2 * time.Second,
+ }
+ if name == "" {
+ name = "Remote"
+ }
+ if sleep != 0 {
+ r.refresh = sleep
+ }
+ remotes[name] = r
+ }
+ if len(remotes) == 0 {
+ log.Println("Remote: no remote URL provided; disabling extension")
+ return nil
+ }
+ RegisterTemp(updateTemp)
+ RegisterMem(updateMem)
+ RegisterCPU(updateUsage)
+
+ // We need to know what we're dealing with, so the following code does two
+ // things, one of them sneakily. It forks off background processes
+ // to periodically pull data from remote sources and cache the results for
+ // when the UI wants it. When it's run the first time, it sets up a WaitGroup
+ // so that it can hold off returning until it's received data from the remote
+ // so that the rest of the program knows how many cores, disks, etc. it needs
+ // to set up UI elements for. After the first run, each process discards the
+ // the wait group.
+ w := &sync.WaitGroup{}
+ for n, r := range remotes {
+ n = n + "-"
+ r.url = r.url
+ var u *url.URL
+ w.Add(1)
+ go func(name string, remote Remote, wg *sync.WaitGroup) {
+ for {
+ res, err := http.Get(remote.url)
+ if err == nil {
+ u, err = url.Parse(remote.url)
+ if err == nil {
+ if res.StatusCode == http.StatusOK {
+ bi := bufio.NewScanner(res.Body)
+ process(name, bi)
+ } else {
+ u.User = nil
+ log.Printf("unsuccessful connection to %s: http status %s", u.String(), res.Status)
+ }
+ } else {
+ log.Print("error processing remote URL")
+ }
+ } else {
+ }
+ res.Body.Close()
+ if wg != nil {
+ wg.Done()
+ wg = nil
+ }
+ time.Sleep(remote.refresh)
+ }
+ }(n, r, w)
+ }
+ w.Wait()
+ return nil
+}
+
+var (
+ _cpuData map[string]int
+ _tempData map[string]int
+ _netData map[string]float64
+ _diskData map[string]float64
+ _memData map[string]MemoryInfo
+)
+
+func process(host string, data *bufio.Scanner) {
+ remoteLock.Lock()
+ for data.Scan() {
+ line := data.Text()
+ if line[0] == '#' {
+ continue
+ }
+ if line[0:6] != _gotop {
+ continue
+ }
+ sub := line[6:]
+ switch {
+ case strings.HasPrefix(sub, _cpu): // int gotop_cpu_CPU0
+ procInt(host, line, sub[4:], _cpuData)
+ case strings.HasPrefix(sub, _temp): // int gotop_temp_acpitz
+ procInt(host, line, sub[5:], _tempData)
+ case strings.HasPrefix(sub, _net): // int gotop_net_recv
+ parts := strings.Split(sub[5:], " ")
+ if len(parts) < 2 {
+ log.Printf(`bad data; not enough columns in "%s"`, line)
+ continue
+ }
+ val, err := strconv.ParseFloat(parts[1], 64)
+ if err != nil {
+ log.Print(err)
+ continue
+ }
+ _netData[host+parts[0]] = val
+ case strings.HasPrefix(sub, _disk): // float % gotop_disk_:dev:mmcblk0p1
+ parts := strings.Split(sub[5:], " ")
+ if len(parts) < 2 {
+ log.Printf(`bad data; not enough columns in "%s"`, line)
+ continue
+ }
+ val, err := strconv.ParseFloat(parts[1], 64)
+ if err != nil {
+ log.Print(err)
+ continue
+ }
+ _diskData[host+parts[0]] = val
+ case strings.HasPrefix(sub, _mem): // float % gotop_memory_Main
+ parts := strings.Split(sub[7:], " ")
+ if len(parts) < 2 {
+ log.Printf(`bad data; not enough columns in "%s"`, line)
+ continue
+ }
+ val, err := strconv.ParseFloat(parts[1], 64)
+ if err != nil {
+ log.Print(err)
+ continue
+ }
+ _memData[host+parts[0]] = MemoryInfo{
+ Total: 100,
+ Used: uint64(100.0 / val),
+ UsedPercent: val,
+ }
+ default:
+ // NOP! This is a metric we don't care about.
+ }
+ }
+ remoteLock.Unlock()
+}
+
+func procInt(host, line, sub string, data map[string]int) {
+ parts := strings.Split(sub, " ")
+ if len(parts) < 2 {
+ log.Printf(`bad data; not enough columns in "%s"`, line)
+ return
+ }
+ val, err := strconv.Atoi(parts[1])
+ if err != nil {
+ log.Print(err)
+ return
+ }
+ data[host+parts[0]] = val
+}
+
+func updateTemp(temps map[string]int) map[string]error {
+ remoteLock.Lock()
+ for name, val := range _tempData {
+ temps[name] = val
+ }
+ remoteLock.Unlock()
+ return nil
+}
+
+// FIXME The units are wrong: getting bytes, assuming they're %
+func updateMem(mems map[string]MemoryInfo) map[string]error {
+ remoteLock.Lock()
+ for name, val := range _memData {
+ mems[name] = val
+ }
+ remoteLock.Unlock()
+ return nil
+}
+
+func updateUsage(cpus map[string]int, _ bool) map[string]error {
+ remoteLock.Lock()
+ for name, val := range _cpuData {
+ cpus[name] = val
+ }
+ remoteLock.Unlock()
+ return nil
+}
+
+func parseConfig(vars map[string]string) map[string]Remote {
+ rv := make(map[string]Remote)
+ for key, value := range vars {
+ if strings.HasPrefix(key, "remote-") {
+ parts := strings.Split(key, "-")
+ if len(parts) == 2 {
+ log.Printf("malformed Remote extension configuration '%s'; must be 'remote-NAME-url' or 'remote-NAME-refresh'", key)
+ continue
+ }
+ name := parts[1]
+ remote, ok := rv[name]
+ if !ok {
+ remote = Remote{}
+ }
+ if parts[2] == "url" {
+ remote.url = value
+ } else if parts[2] == "refresh" {
+ sleep, err := strconv.Atoi(value)
+ if err != nil {
+ log.Printf("illegal Remote extension value for %s: '%s'. Must be a duration in seconds, e.g. '2'", key, value)
+ continue
+ }
+ remote.refresh = time.Duration(sleep) * time.Second
+ } else {
+ log.Printf("bad configuration option for Remote extension: '%s'; must be 'remote-NAME-url' or 'remote-NAME-refresh'", key)
+ continue
+ }
+ rv[name] = remote
+ }
+ }
+ return rv
+}
+
+const (
+ _gotop = "gotop_"
+ _cpu = "cpu_"
+ _temp = "temp_"
+ _net = "net_"
+ _disk = "disk_"
+ _mem = "memory_"
+)
diff --git a/dicts/de_DE.toml b/dicts/de_DE.toml
index 93138f1..9a8a933 100644
--- a/dicts/de_DE.toml
+++ b/dicts/de_DE.toml
@@ -4,27 +4,27 @@ total="Gesamt"
[help]
-paths="Nach ladbaren Farbschemata und Layouts sowie der Konfigurationsdatei wird in der folgenden Reihenfolge gesucht:"
+paths="Nach Farbschemata und Layouts sowie der Konfigurationsdatei wird in der folgenden Reihenfolge gesucht:"
log="Die Protokolldatei befindet sich in {0}"
-written="Konfiguration geschrieben auf {0}"
+written="Konfiguration geschrieben nach {0}"
help="""
Beenden: q oder <C-c>
Prozessnavigation:
- - k und <Up>: oben
- - j und <Down>: nieder
+ - k und <Up>: Zeile nach oben
+ - j und <Down>: Zeile nach unten
- <C-u>: halbe Seite nach oben
- <C-d>: halbe Seite nach unten
- <C-b>: ganze Seite nach oben
- <C-f>: ganze Seite nach unten
- - gg und <Home>: nach oben springen
- - G und <End>: nach unten springen
+ - gg und <Home>: an den Anfang springen
+ - G und <End>: an das Ende springen
Process actions:
- <Tab>: Prozessgruppierung umschalten
- - dd: Beende einen ausgewählten Prozess oder eine Gruppe von Prozessen mit SIGTERM (15)
- - d3: Beende einen ausgewählten Prozess oder eine Gruppe von Prozessen mit SIGQUIT (3)
- - d9: töte einen ausgewählten Prozess oder eine Gruppe von Prozessen mit SIGKILL (9)
+ - dd: Beende ausgewählten Prozess oder Gruppe von Prozessen mit SIGTERM (15)
+ - d3: Beende ausgewählten Prozess oder Gruppe von Prozessen mit SIGQUIT (3)
+ - d9: töte ausgewählten Prozess oder Gruppe von Prozessen mit SIGKILL (9)
Prozesssortierung:
- c: CPU
@@ -38,11 +38,11 @@ Prozessfilterung:
- <C-c> und <Escape>: Filter löschen
CPU- und Mem-Graph-Skalierung:
- - h: skalieren in
- - l: skalieren
+ - h: Skalierung vergrößern
+ - l: Skalierung verkleinern
Netzwerk:
- - b: Umschalten zwischen MBit / s und skalierten Bytes pro Sekunde
+ - b: Umschalten zwischen MBit/s und skalierten Bytes pro Sekunde
"""
# ÜBERSETZER: Bitte übersetzen Sie die Layout-**Namen** nicht
layouts = """Eingebaute Layouts:
@@ -62,67 +62,69 @@ colorschemes = """Eingebaute Farbschemata:
nord"""
# ÜBERSETZER: Bitte übersetzen Sie die Widget-**Namen** nicht
widgets = """Widgets, die in Layouts verwendet werden können:
- cpu - CPU-Lastdiagramm
- mem - Physische & Swap-Speicher verwenden Grafik
+ cpu - CPU-Auslastungsgraph
+ mem - Physischer- & Swap-Speicher Auslastungsgraph
temp - Sensortemperaturen
- disk - Verwendung der physischen Festplattenpartition
- power - Eine Batteriestange
+ disk - Belegung der physischen Festplattenpartitionen
+ power - Batterieladung
net - Netzwerklast
procs - Interaktive Prozessliste"""
[args]
-help="Hilfetext anzeigen."
-color="Ein Farbschema feststellen."
-scale="Stellen den Skalierungsfaktor ein, >0"
-version="Zeigen die Version aus und beenden."
-percpu="Zeigen Sie jede CPU im CPU-Widget an."
+help="Diese Hilfe anzeigen."
+color="Farbschema einstellen."
+scale="Skalierungsfaktor der Graphen, >0"
+version="Versionsangabe und Beenden."
+percpu="Jede CPU im CPU-Widget anzeigen."
cpuavg="Durchschnittliche CPU im CPU-Widget anzeigen."
temp="Temperaturen in Fahrenheit anzeigen."
-statusbar="Zeigen Sie eine Statusleiste mit der Uhrzeit an."
+statusbar="Statusleiste mit Uhrzeit anzeigen."
rate="Frequenz aktualisieren. Die meisten Zeiteinheiten werden akzeptiert. \"1m\" = jede Minute aktualisieren. \"100 ms\" = alle 100 ms aktualisieren."
-layout="Name der Layoutspezifikationsdatei für die Benutzeroberfläche. Verwenden Sie \"-\", um zu leiten."
-net="Wählen Sie die Netzwerkschnittstelle. Mehrere Schnittstellen können mit durch Kommas getrennten Werten definiert werden. Schnittstellen können auch mit ignoriert werden \"!\""
-export="Aktivieren Sie Metriken für den Export auf dem angegebenen Port."
-mbps="Netzwerkrate als MBit / s anzeigen."
-test="Führt Tests aus und beendet das Programm mit Erfolgs- / Fehlercode."
-conffile="Konfigurationsdatei, die anstelle der Standardeinstellung verwendet werden soll (MUSS DAS ERSTE ARGUMENT SEIN)"
+layout="Name der Layoutspezifikationsdatei für die Benutzeroberfläche. \"-\" liest aus Standard-Eingabe."
+net="Netzwerkschnittstelle auswählen. Mehrere Schnittstellen können durch Kommata getrennt werden. Schnittstellen mit \"!\" werden ignoriert."
+export="Metriken für den Export auf dem angegebenen Port aktivieren."
+mbps="Netzwerkrate als MBit/s anzeigen."
+test="Tests ausführen und mit Erfolgs- oder Fehlercode beenden."
+conffile="Konfigurationsdatei, die anstelle der Standardeinstellung verwendet werden soll (muss ERSTES ARGUMENT sein)."
+nvidia="NVidia-GPU-Metriken aktivieren."
+nvidiarefresh="Frequenz aktualisieren. Die meisten Zeiteinheiten werden akzeptiert."
list="""
-List <devices|layouts|colorschemes|paths|keys|langs>
- devices: Druckt Gerätenamen für filterbare Widgets aus
- layouts: Listet integrierte Layouts auf
- colorschemes: Listet integrierte Farbschemata auf
- paths: Listen Sie die Suchpfade für Konfigurationsdateien auf
- widgets: Widgets, die in einem Layout verwendet werden können
- keys: Zeigen Sie die Tastaturbindungen an.
- langs: Unterstützte Sprachübersetzungen anzeigen."""
-write="Schreiben Sie eine Standardkonfigurationsdatei."
+Auflisten von <devices|layouts|colorschemes|paths|keys|langs>
+ devices: Gerätenamen für filterbare Widgets
+ layouts: eingebaute Layouts
+ colorschemes: eingebaute Farbschemata
+ paths: Suchpfade für Konfigurationsdateien
+ widgets: verwendbare Widgets für Layouts
+ keys: Tastaturkürzel
+ langs: Übersetzungen"""
+write="Standard-Konfigurationsdatei schreiben."
[config.err]
-configsyntax="0| schlechte Syntax der Konfigurationsdatei; sollte KEY=VALUE sein, war {0}"
+configsyntax="0| Fehlerhafte Syntax der Konfigurationsdatei: sollte KEY=VALUE sein, war {0}"
deprecation="1| Zeile {0}: '{1}' ist veraltet. Ignoriert {1}={2}"
line="2| Zeile #{0}: {1}"
-tempscale="3| ungültiger TempScale-Wert {0}"
+tempscale="3| Ungültiger TempScale-Wert {0}"
[error]
-configparse="4| Konfigurationsdatei konnte nicht analysiert werden: {0}"
-cliparse="5| Analysieren von CLI-Argumenten: {0}"
-logsetup="6| Protokolldatei konnte nicht eingerichtet werden: {0}"
-unknownopt="7| Unbekannte Option \"{0}\"; Probieren Sie layouts, colorschemes, keys, paths oder devices aus\n"
+configparse="4| Fehler beim Einlesen der Konfigurationsdatei: {0}"
+cliparse="5| Einlesen der CLI-Argumente: {0}"
+logsetup="6| Fehler beim Einrichten der Protokolldatei: {0}"
+unknownopt="7| Unbekannte Option \"{0}\". Mögliche Optionen: layouts, colorschemes, keys, paths, devices\n"
writefail="8| Konfigurationsdatei konnte nicht geschrieben werden: {0}"
-checklog="9| aufgetretene Fehler; von {0}:"
+checklog="9| Aufgetretene Fehler; von {0}:"
metricsetup="10| Fehler beim Einrichten von {0}-Metriken: {1}"
nometrics="11| Keine Metriken für {0} {1}"
fatalfetch="12| Schwerwiegender Fehler beim Abrufen von {0}-Informationen: {1}"
-recovfetch="13| behebbarer Fehler beim Abrufen von {0}-Informationen; überspringen {0}: {1}"
+recovfetch="13| Behebbarer Fehler beim Abrufen von {0}-Informationen; überspringen {0}: {1}"
nodevfound="14| Keine verwendbare {0} gefunden"
setuperr="15| Fehler beim Einrichten {0}: {1}"
colorschemefile="16| Farbschemadatei konnte nicht gefunden werden {0} in {1}"
colorschemeread="17| Farbschemadatei konnte nicht gelesen werden {0}: {1}"
colorschemeparse="18| Farbschemadatei konnte nicht analysiert werden: {0}"
-findlayout="19| Farbschemadatei konnte nicht gelesen werden {0}: {1}"
+findlayout="19| Layoutdatei konnte nicht gefunden werden {0}: {1}"
logopen="20| Protokolldatei konnte nicht geöffnet werden {0}: {1}"
table="21| Tabellen-Widget TopRow-Wert kleiner als 0. TopRow: {0}"
nohostname="22| Hostname konnte nicht abgerufen werden: {0}"
@@ -134,7 +136,7 @@ slashes="25 | Layoutwarnung in Zeile {0}: zu viele '/' in Wort {1}; zusätzliche
[widget.label]
disk=" Festplattennutzung "
-cpu=" CPU auslastung "
+cpu=" CPU-Auslastung "
gauge=" Leistungspegel "
battery=" Batteriestatus "
batt=" Batterie "
@@ -145,18 +147,18 @@ mem=" Speichernutzung "
[widget.net.err]
-netactivity="26 | Netzwerkaktivität von gopsutil konnte nicht abgerufen werden: {0}"
+netactivity="26 | Fehler beim Abrufen der Netzwerkaktivität von gopsutil: {0}"
negvalrecv="27 | Fehler: negativer Wert für kürzlich empfangene Netzwerkdaten von gopsutil. RecentBytesRecv: {0}"
negvalsent="28 | Fehler: negativer Wert für kürzlich gesendete Netzwerkdaten von gopsutil. RecentBytesSent: {0}"
[widget.disk]
-disk="Scheibe"
-mount="Montieren"
-used="Gebraucht"
-free="Kostenlos"
-rs="R / s"
-ws="W / s"
+disk="Festplatte"
+mount="Einhängepunkt"
+used="Benutzt"
+free="Frei"
+rs="R/s"
+ws="W/s"
[widget.proc]
@@ -169,14 +171,14 @@ cpu="CPU%"
mem="Mem%"
pid="PID"
[widget.proc.err]
-count="29 | CPU-Anzahl konnte nicht von gopsutil abgerufen werden: {0}"
-retrieve="30 | Prozesse konnten nicht abgerufen werden: {0}"
-ps="31 | Befehl 'ps' konnte nicht ausgeführt werden: {0}"
-gopsutil="32 | Prozesse konnten nicht von gopsutil abgerufen werden: {0}"
-pidconv="33 | Konvertierung der PID in int: {0} fehlgeschlagen. Linie 1}"
-cpuconv="34 | Konvertierung der CPU-Auslastung in float fehlgeschlagen: {0}. Linie 1}"
-memconv="35 | Die Verwendung von Mem konnte nicht in float konvertiert werden: {0}. Linie 1}"
-getcmd="36 | Prozessbefehl von gopsutil konnte nicht abgerufen werden: {0}. psProc: {1}. i: {2}. pid: {3}"
-cpupercent="37 | Fehler beim Abrufen der Prozess-CPU-Nutzung von gopsutil: {0}. psProc: {1}. i: {2}. pid: {3}"
-mempercent="38 | Prozessspeicherauslastung konnte nicht von gopsutil abgerufen werden: {0}. psProc: {1}. i: {2}. pid: {3}"
+count="29 | Fehler beim Abrufen der CPU-Anzahl von gopsutil: {0}"
+retrieve="30 | Fehler beim Abrufen der Prozesse: {0}"
+ps="31 | Fehler bei Ausführung von 'ps': {0}"
+gopsutil="32 | Fehler beim Abrufen der Prozesse von gopsutil: {0}"
+pidconv="33 | Konvertierung der PID in int: {0} fehlgeschlagen. Zeile 1}"
+cpuconv="34 | Konvertierung der CPU-Auslastung in float fehlgeschlagen: {0}. Zeile 1}"
+memconv="35 | Konvertierung der Speicher-Auslastung in float fehlgeschlagen: {0}. Zeile 1}"
+getcmd="36 | Fehler beim Abrufen des Prozessbefehls von gopsutil: {0}. psProc: {1}. i: {2}. pid: {3}"
+cpupercent="37 | Fehler beim Abrufen der Prozess-CPU-Auslastung von gopsutil: {0}. psProc: {1}. i: {2}. pid: {3}"
+mempercent="38 | Fehler beim Abrufen der Prozess-Speicher-Auslastung von gopsutil: {0}. psProc: {1}. i: {2}. pid: {3}"
parse="39 | Ausgabe konnte nicht analysiert werden: {0}"
diff --git a/dicts/en_US.toml b/dicts/en_US.toml
index bf1771b..476556b 100644
--- a/