mirror of
https://github.com/Alexander-D-Karpov/netfetch.git
synced 2026-03-16 22:07:03 +03:00
352 lines
7.1 KiB
Go
352 lines
7.1 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"netfetch/assets"
|
|
"netfetch/internal/collector"
|
|
"netfetch/internal/config"
|
|
"netfetch/internal/display"
|
|
"netfetch/internal/handler"
|
|
"netfetch/internal/logo"
|
|
)
|
|
|
|
const (
|
|
defaultPort = 22828
|
|
defaultLogoDir = "logos"
|
|
)
|
|
|
|
type Mode int
|
|
|
|
const (
|
|
ModeServe Mode = iota
|
|
ModeShow
|
|
ModeConnect
|
|
ModeHelp
|
|
)
|
|
|
|
func init() {
|
|
logo.EmbeddedLogos = assets.LogosFS
|
|
}
|
|
|
|
func main() {
|
|
if len(os.Args) > 1 && (os.Args[1] == "-h" || os.Args[1] == "--help" || os.Args[1] == "help") {
|
|
printHelp()
|
|
return
|
|
}
|
|
|
|
var (
|
|
port int
|
|
configFile string
|
|
logoDir string
|
|
timeout int
|
|
showAll bool
|
|
)
|
|
|
|
flagSet := flag.NewFlagSet("netfetch", flag.ExitOnError)
|
|
flagSet.IntVar(&port, "port", 0, "Port for server/client")
|
|
flagSet.StringVar(&configFile, "config", "", "Path to config file")
|
|
flagSet.StringVar(&logoDir, "logo-dir", "", "Directory containing logo files")
|
|
flagSet.IntVar(&timeout, "timeout", 5, "Connection timeout in seconds")
|
|
flagSet.BoolVar(&showAll, "all", false, "Show all modules")
|
|
|
|
mode, host, args := parseArgs(os.Args[1:])
|
|
|
|
flagSet.Parse(args)
|
|
|
|
var modules []string
|
|
if mode == ModeShow {
|
|
modules = flagSet.Args()
|
|
}
|
|
|
|
switch mode {
|
|
case ModeServe:
|
|
runServe(port, configFile, logoDir)
|
|
case ModeShow:
|
|
runShow(configFile, logoDir, showAll, modules)
|
|
case ModeConnect:
|
|
runConnect(host, port, timeout)
|
|
case ModeHelp:
|
|
printHelp()
|
|
}
|
|
}
|
|
|
|
func withBaseModules(mods []string) []string {
|
|
base := []string{"os"}
|
|
out := make([]string, 0, len(mods)+len(base))
|
|
out = append(out, mods...)
|
|
|
|
for _, b := range base {
|
|
found := false
|
|
for _, m := range mods {
|
|
if m == b {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
out = append(out, b)
|
|
}
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
func parseArgs(args []string) (Mode, string, []string) {
|
|
if len(args) == 0 {
|
|
return ModeServe, "", args
|
|
}
|
|
|
|
firstArg := args[0]
|
|
|
|
if firstArg == "serve" {
|
|
return ModeServe, "", args[1:]
|
|
}
|
|
|
|
if firstArg == "show" {
|
|
return ModeShow, "", args[1:]
|
|
}
|
|
|
|
if firstArg == "connect" {
|
|
if len(args) > 1 {
|
|
return ModeConnect, args[1], args[2:]
|
|
}
|
|
log.Fatal("connect mode requires a host argument")
|
|
}
|
|
|
|
if !isFlag(firstArg) {
|
|
return ModeConnect, firstArg, args[1:]
|
|
}
|
|
|
|
return ModeServe, "", args
|
|
}
|
|
|
|
func isFlag(arg string) bool {
|
|
return len(arg) > 0 && arg[0] == '-'
|
|
}
|
|
|
|
func runShow(configFile, logoDir string, showAll bool, modules []string) {
|
|
cfg := loadConfig(configFile, logoDir, 0)
|
|
|
|
if showAll {
|
|
cfg.ActiveModules = config.GetDefaultModules()
|
|
} else if len(modules) > 0 {
|
|
cfg.ActiveModules = modules
|
|
}
|
|
|
|
logos, err := logo.LoadAll(cfg.LogoDir)
|
|
if err != nil {
|
|
log.Fatalf("Failed to load logos: %v", err)
|
|
}
|
|
|
|
collectorModules := withBaseModules(cfg.ActiveModules)
|
|
|
|
c := collector.New(collectorModules)
|
|
c.CollectDynamicInfo()
|
|
|
|
if err := display.ShowColorized(c, logos, cfg); err != nil {
|
|
log.Fatalf("Error displaying info: %v", err)
|
|
}
|
|
}
|
|
|
|
func runServe(port int, configFile, logoDir string) {
|
|
cfg := loadConfig(configFile, logoDir, port)
|
|
|
|
logos, err := logo.LoadAll(cfg.LogoDir)
|
|
if err != nil {
|
|
log.Fatalf("Failed to load logos: %v", err)
|
|
}
|
|
log.Printf("Loaded %d logos", len(logos))
|
|
|
|
collectorModules := withBaseModules(cfg.ActiveModules)
|
|
|
|
c := collector.New(collectorModules)
|
|
h := handler.New(c, logos, cfg)
|
|
|
|
sigChan := make(chan os.Signal, 1)
|
|
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
server := &http.Server{
|
|
Addr: cfg.ListenAddress,
|
|
Handler: h,
|
|
}
|
|
|
|
go func() {
|
|
log.Printf("Starting server on %s", cfg.ListenAddress)
|
|
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
log.Fatalf("Server failed: %v", err)
|
|
}
|
|
}()
|
|
|
|
<-sigChan
|
|
log.Println("Shutting down server...")
|
|
if err := server.Close(); err != nil {
|
|
log.Printf("Error during shutdown: %v", err)
|
|
}
|
|
}
|
|
|
|
func runConnect(host string, port, timeout int) {
|
|
if host == "" {
|
|
log.Fatal("No host specified for connect mode")
|
|
}
|
|
|
|
if port == 0 {
|
|
port = defaultPort
|
|
}
|
|
|
|
fullHost := host
|
|
if !containsPort(host) {
|
|
fullHost = fmt.Sprintf("%s:%d", host, port)
|
|
}
|
|
|
|
client := &http.Client{
|
|
Timeout: time.Duration(timeout) * time.Second,
|
|
}
|
|
|
|
resp, err := client.Get(fmt.Sprintf("http://%s", fullHost))
|
|
if err != nil {
|
|
log.Fatalf("Failed to connect to %s: %v", fullHost, err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
_, err = io.Copy(os.Stdout, resp.Body)
|
|
if err != nil {
|
|
log.Fatalf("Error reading response: %v", err)
|
|
}
|
|
}
|
|
|
|
func loadConfig(configFile, logoDir string, port int) *config.Config {
|
|
var cfg *config.Config
|
|
|
|
if configFile == "" {
|
|
configFile = "config.yaml"
|
|
}
|
|
|
|
loadedCfg, err := config.Load(configFile)
|
|
if err != nil {
|
|
log.Printf("Config file not found, using defaults")
|
|
cfg = getDefaultConfig()
|
|
} else {
|
|
cfg = loadedCfg
|
|
}
|
|
|
|
if logoDir != "" {
|
|
cfg.LogoDir = logoDir
|
|
}
|
|
|
|
if port > 0 {
|
|
cfg.ListenAddress = fmt.Sprintf(":%d", port)
|
|
} else if cfg.ListenAddress == "" {
|
|
cfg.ListenAddress = fmt.Sprintf(":%d", defaultPort)
|
|
}
|
|
|
|
if cfg.LogoDir == "" {
|
|
cfg.LogoDir = ""
|
|
}
|
|
|
|
return cfg
|
|
}
|
|
|
|
func getDefaultConfig() *config.Config {
|
|
return &config.Config{
|
|
ListenAddress: fmt.Sprintf(":%d", defaultPort),
|
|
ActiveModules: []string{
|
|
"os", "kernel", "uptime", "packages", "shell", "resolution",
|
|
"de", "wm", "theme", "icons", "terminal", "cpu", "gpu",
|
|
"memory", "disk", "swap", "battery", "locale",
|
|
},
|
|
DefaultLogo: "linux",
|
|
LogoDir: "",
|
|
}
|
|
}
|
|
|
|
func containsPort(host string) bool {
|
|
for i := len(host) - 1; i >= 0; i-- {
|
|
if host[i] == ':' {
|
|
return true
|
|
}
|
|
if host[i] == ']' {
|
|
return false
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func printHelp() {
|
|
fmt.Println(`netfetch - Display system information
|
|
|
|
USAGE:
|
|
netfetch [MODE] [OPTIONS] [HOST]
|
|
|
|
MODES:
|
|
(default)
|
|
Start HTTP server (default mode when no arguments)
|
|
|
|
serve
|
|
Start HTTP server to serve system information
|
|
netfetch serve [OPTIONS]
|
|
|
|
show
|
|
Display local system information (dry run)
|
|
netfetch show [OPTIONS] [MODULE ...]
|
|
|
|
connect <host>
|
|
Connect to a remote netfetch server
|
|
netfetch connect <host> [OPTIONS]
|
|
netfetch <host> [OPTIONS]
|
|
|
|
OPTIONS:
|
|
-port int
|
|
Port number for server/client (default: 22828)
|
|
|
|
-config string
|
|
Path to config file (default: config.yaml)
|
|
|
|
-logo-dir string
|
|
Directory containing logo files (uses embedded logos by default)
|
|
|
|
-timeout int
|
|
Connection timeout in seconds (default: 5)
|
|
|
|
-all
|
|
Show all modules (ignore active_modules from config)
|
|
|
|
-h, -help, help
|
|
Show this help message
|
|
|
|
EXAMPLES:
|
|
Start server (default):
|
|
netfetch
|
|
netfetch serve
|
|
|
|
Start server on custom port:
|
|
netfetch -port 8080
|
|
netfetch serve -port 8080
|
|
|
|
Show local system info (active modules from config):
|
|
netfetch show
|
|
|
|
Show only specific modules:
|
|
netfetch show cpu gpu
|
|
|
|
Show all modules:
|
|
netfetch show -all
|
|
|
|
Connect to remote server:
|
|
netfetch example.com
|
|
netfetch connect example.com
|
|
netfetch example.com:8080 -timeout 10
|
|
|
|
Use custom config:
|
|
netfetch -config /path/to/config.yaml
|
|
netfetch show -config custom.yaml -logo-dir /path/to/logos`)
|
|
}
|