add LinuxStatus()

Signed-off-by: Jeff Carr <jcarr@wit.com>
This commit is contained in:
Jeff Carr 2024-01-06 05:24:11 -06:00
parent a808bb5518
commit 4529b473dc
12 changed files with 784 additions and 11 deletions

View File

@ -25,18 +25,7 @@ func getHostname() {
me.status.SetHostname(s)
dn := run("domainname")
if (me.domainname.S != dn) {
log.Log(CHANGE, "domainname has changed from", me.domainname.S, "to", dn)
me.domainname.SetText(dn)
me.changed = true
}
hshort := run("hostname -s")
if (me.hostshort.S != hshort) {
log.Log(CHANGE, "hostname -s has changed from", me.hostshort.S, "to", hshort)
me.hostshort.SetText(hshort)
me.changed = true
}
var test string
test = hshort + "." + dn

61
linuxstatus/args.go Normal file
View File

@ -0,0 +1,61 @@
package linuxstatus
/*
this enables command line options from other packages like 'gui' and 'log'
*/
import (
"go.wit.com/log"
)
var NOW log.LogFlag
var NET log.LogFlag
var DNS log.LogFlag
var PROC log.LogFlag
var SPEW log.LogFlag
var CHANGE log.LogFlag
var STATUS log.LogFlag
func init() {
NOW.B = false
NOW.Name = "NOW"
NOW.Subsystem = "cpdns"
NOW.Desc = "temp debugging stuff"
NOW.Register()
NET.B = false
NET.Name = "NET"
NET.Subsystem = "cpdns"
NET.Desc = "Network logging"
NET.Register()
DNS.B = false
DNS.Name = "DNS"
DNS.Subsystem = "cpdns"
DNS.Desc = "dnsStatus.update()"
DNS.Register()
PROC.B = false
PROC.Name = "PROC"
PROC.Subsystem = "cpdns"
PROC.Desc = "/proc logging"
PROC.Register()
SPEW.B = false
SPEW.Name = "SPEW"
SPEW.Subsystem = "cpdns"
SPEW.Desc = "spew logging"
SPEW.Register()
CHANGE.B = false
CHANGE.Name = "CHANGE"
CHANGE.Subsystem = "cpdns"
CHANGE.Desc = "show droplet state changes"
CHANGE.Register()
STATUS.B = false
STATUS.Name = "STATUS"
STATUS.Subsystem = "cpdns"
STATUS.Desc = "updateStatus()"
STATUS.Register()
}

34
linuxstatus/common.go Normal file
View File

@ -0,0 +1,34 @@
// This creates a simple hello world window
package linuxstatus
import (
"go.wit.com/log"
)
func (hs *LinuxStatus) Show() {
log.Log(CHANGE, "linuxStatus.Show() window")
hs.window.Show()
hs.hidden = false
}
func (hs *LinuxStatus) Hide() {
log.Log(CHANGE, "linuxStatus.Hide() window")
hs.window.Hide()
hs.hidden = true
}
func (hs *LinuxStatus) Toggle() {
log.Log(CHANGE, "linuxStatus.Toggle() window")
if hs.hidden {
hs.window.Show()
} else {
hs.window.Hide()
}
}
func (hs *LinuxStatus) Ready() bool {
if me == nil {return false}
if hs == nil {return false}
if hs.window == nil {return false}
return me.ready
}

34
linuxstatus/draw.go Normal file
View File

@ -0,0 +1,34 @@
// This creates a simple hello world window
package linuxstatus
import (
"go.wit.com/gui/gadgets"
)
// creates the actual widgets.
// it's assumed you are always passing in a box
func draw(ls *LinuxStatus) {
if ! ls.Ready() {return}
ls.group = ls.window.Box().NewGroup("Real Stuff")
ls.grid = ls.group.NewGrid("gridnuts", 2, 2)
ls.grid.SetNext(1,1)
ls.hostshort = gadgets.NewOneLiner(ls.grid, "hostname -s")
ls.domainname = gadgets.NewOneLiner(ls.grid, "domain name")
ls.NSrr = gadgets.NewOneLiner(ls.grid, "NS records =")
ls.uid = gadgets.NewOneLiner(ls.grid, "UID =")
ls.IPv4 = gadgets.NewOneLiner(ls.grid, "Current IPv4 =")
ls.IPv6 = gadgets.NewOneLiner(ls.grid, "Current IPv6 =")
ls.workingIPv6 = gadgets.NewOneLiner(ls.grid, "Real IPv6 =")
// ls.nics = gadgets.NewOneLiner(ls.grid, "network intefaces =")
ls.grid.NewLabel("interfaces =")
ls.Interfaces = ls.grid.NewCombobox("Interfaces")
ls.speedActual = gadgets.NewOneLiner(ls.grid, "refresh speed =")
ls.grid.Margin()
ls.grid.Pad()
}

86
linuxstatus/hostname.go Normal file
View File

@ -0,0 +1,86 @@
// figures out if your hostname is valid
// then checks if your DNS is setup correctly
package linuxstatus
import (
"go.wit.com/log"
"go.wit.com/shell"
// will try to get this hosts FQDN
"github.com/Showmax/go-fqdn"
)
func (ls *LinuxStatus) GetDomainName() string {
if ! me.Ready() {return ""}
return me.domainname.Get()
}
func (ls *LinuxStatus) setDomainName(dn string) {
if ! me.Ready() {return}
me.domainname.Set(dn)
}
func getHostname() {
var err error
var s string = "gui.Label == nil"
s, err = fqdn.FqdnHostname()
if (err != nil) {
log.Error(err, "FQDN hostname error")
return
}
log.Warn("full hostname should be:", s)
dn := run("domainname")
if (me.domainname.Get() != dn) {
log.Log(CHANGE, "domainname has changed from", me.GetDomainName(), "to", dn)
me.setDomainName(dn)
me.changed = true
}
hshort := run("hostname -s")
if (me.hostshort.Get() != hshort) {
log.Log(CHANGE, "hostname -s has changed from", me.hostshort.Get(), "to", hshort)
me.hostshort.Set(hshort)
me.changed = true
}
/*
var test string
test = hshort + "." + dn
if (me.status.GetHostname() != test) {
log.Log(CHANGE, "me.hostname", me.status.GetHostname(), "does not equal", test)
if (me.hostnameStatus.S != "BROKEN") {
log.Log(CHANGE, "me.hostname", me.status.GetHostname(), "does not equal", test)
me.changed = true
me.hostnameStatus.SetText("BROKEN")
}
} else {
if (me.hostnameStatus.S != "VALID") {
log.Log(CHANGE, "me.hostname", me.status.GetHostname(), "is valid")
me.hostnameStatus.SetText("VALID")
me.changed = true
}
}
*/
}
// returns true if the hostname is good
// check that all the OS settings are correct here
// On Linux, /etc/hosts, /etc/hostname
// and domainname and hostname
func goodHostname() bool {
hostname := shell.Chomp(shell.Cat("/etc/hostname"))
log.Log(NOW, "hostname =", hostname)
hs := run("hostname -s")
dn := run("domainname")
log.Log(NOW, "hostname short =", hs, "domainname =", dn)
tmp := hs + "." + dn
if (hostname == tmp) {
log.Log(NOW, "hostname seems to be good", hostname)
return true
}
return false
}

44
linuxstatus/linuxloop.go Normal file
View File

@ -0,0 +1,44 @@
// GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007
// Copyright (c) 2023 WIT.COM, Inc.
// This is a control panel for DNS
package linuxstatus
import (
"strconv"
"go.wit.com/log"
)
func linuxLoop() {
me.changed = false
duration := timeFunction(getHostname)
log.Info("getHostname() execution Time: ", duration, "me.changed =", me.changed)
duration = timeFunction(scanInterfaces)
log.Log(NET, "scanInterfaces() execution Time: ", duration)
for i, t := range me.ifmap {
log.Log(NET, strconv.Itoa(i) + " iface = " + t.iface.Name)
}
var aaaa []string
aaaa = dhcpAAAA()
var all string
for _, s := range aaaa {
log.Log(NET, "my actual AAAA = ",s)
all += s + "\n"
}
// me.IPv6.SetText(all)
/*
processName := getProcessNameByPort(53)
fmt.Println("Process with port 53:", processName)
commPath := filepath.Join("/proc", proc.Name(), "comm")
comm, err := ioutil.ReadFile(commPath)
if err != nil {
return "", err // Error reading the process name
}
return strings.TrimSpace(string(comm)), nil
*/
}

292
linuxstatus/net.go Normal file
View File

@ -0,0 +1,292 @@
// This creates a simple hello world window
package linuxstatus
import (
// "log"
"net"
"strings"
"go.wit.com/log"
)
// this doesn't work
/*
func watchNetworkInterfaces() {
// Get list of network interfaces
interfaces, _ := net.Interfaces()
// Set up a notification channel
notification := make(chan net.Interface)
log.Log(NET, "watchNet()")
// Start goroutine to watch for changes
go func() {
log.Log(NET, "watchNet() func")
for {
log.Log(NET, "forever loop start")
// Check for changes in each interface
for _, i := range interfaces {
log.Log(NET, "something on i =", i)
if status := i.Flags & net.FlagUp; status != 0 {
notification <- i
log.Log(NET, "something on i =", i)
}
}
log.Log(NET, "forever loop end")
}
}()
}
*/
func IsIPv6(address string) bool {
return strings.Count(address, ":") >= 2
}
func (t *IPtype) IsReal() bool {
if (t.ip.IsPrivate() || t.ip.IsLoopback() || t.ip.IsLinkLocalUnicast()) {
log.Log(NET, "\t\tIP is Real = false")
return false
} else {
log.Log(NET, "\t\tIP is Real = true")
return true
}
}
func IsReal(ip *net.IP) bool {
if (ip.IsPrivate() || ip.IsLoopback() || ip.IsLinkLocalUnicast()) {
log.Log(NET, "\t\tIP is Real = false")
return false
} else {
log.Log(NET, "\t\tIP is Real = true")
return true
}
}
func renameInterface(i *net.Interface) {
/*
/sbin/ip link set eth1 down
/sbin/ip link set eth1 name eth123
/sbin/ip link set eth123 up
*/
}
// Will figure out if an interface was just added
func checkInterface(i net.Interface) {
val, ok := me.ifmap[i.Index]
if ! ok {
log.Info(i.Name, "is a new network interface. The linux kernel index =", i.Index)
me.ifmap[i.Index] = new(IFtype)
me.ifmap[i.Index].gone = false
me.ifmap[i.Index].iface = &i
me.changed = true
if (me.Interfaces != nil) {
me.Interfaces.AddText(i.Name)
me.Interfaces.SetText(i.Name)
}
return
}
me.ifmap[i.Index].gone = false
log.Log(NET, "me.ifmap[i] does exist. Need to compare everything.", i.Index, i.Name, val.iface.Index, val.iface.Name)
if (val.iface.Name != i.Name) {
log.Info(val.iface.Name, "has changed to it's name to", i.Name)
me.ifmap[i.Index].iface = &i
me.changed = true
if (me.Interfaces != nil) {
me.Interfaces.AddText(i.Name)
me.Interfaces.SetText(i.Name)
}
return
}
}
/*
These are the real IP address you have been
given from DHCP
*/
func dhcpAAAA() []string {
var aaaa []string
for s, t := range me.ipmap {
if (t.IsReal()) {
if (t.ipv6) {
aaaa = append(aaaa, s)
}
}
}
return aaaa
}
func realA() []string {
var a []string
for s, t := range me.ipmap {
if (t.IsReal()) {
if (t.ipv4) {
a = append(a, s)
}
}
}
return a
}
func checkDNS() (map[string]*IPtype, map[string]*IPtype) {
var ipv4s map[string]*IPtype
var ipv6s map[string]*IPtype
ipv4s = make(map[string]*IPtype)
ipv6s = make(map[string]*IPtype)
for s, t := range me.ipmap {
i := t.iface
ipt := "IPv4"
if (t.ipv6) {
ipt = "IPv6"
}
if (t.IsReal()) {
log.Info("\tIP is Real ", ipt, i.Index, i.Name, s)
if (t.ipv6) {
ipv6s[s] = t
} else {
ipv4s[s] = t
}
} else {
log.Info("\tIP is not Real", ipt, i.Index, i.Name, s)
}
}
return ipv6s, ipv4s
}
// Will figure out if an IP address is new
func checkIP(ip *net.IPNet, i net.Interface) bool {
log.Log(NET, "\t\taddr.(type) = *net.IPNet")
log.Log(NET, "\t\taddr.(type) =", ip)
var realip string
realip = ip.IP.String()
val, ok := me.ipmap[realip]
if ok {
log.Log(NET, val.ipnet.IP.String(), "is already a defined IP address")
me.ipmap[realip].gone = false
return false
}
me.ipmap[realip] = new(IPtype)
me.ipmap[realip].gone = false
me.ipmap[realip].ipv4 = true
me.ipmap[realip].ipnet = ip
me.ipmap[realip].ip = ip.IP
me.ipmap[realip].iface = &i
t := "IPv4"
if (IsIPv6(ip.String())) {
me.ipmap[realip].ipv6 = true
me.ipmap[realip].ipv4 = false
t = "IPv6"
if (me.IPv6 != nil) {
me.IPv6.Set(realip)
}
} else {
me.ipmap[realip].ipv6 = false
me.ipmap[realip].ipv4 = true
if (me.IPv4 != nil) {
me.IPv4.Set(realip)
}
}
if (IsReal(&ip.IP)) {
log.Info("\tIP is Real ", t, i.Index, i.Name, realip)
} else {
log.Info("\tIP is not Real", t, i.Index, i.Name, realip)
}
log.Log(NET, "\t\tIP is IsPrivate() =", ip.IP.IsPrivate())
log.Log(NET, "\t\tIP is IsLoopback() =", ip.IP.IsLoopback())
log.Log(NET, "\t\tIP is IsLinkLocalUnicast() =", ip.IP.IsLinkLocalUnicast())
// log.Info("HERE HERE", "realip =", realip, "me.ip[realip]=", me.ipmap[realip])
return true
}
func scanInterfaces() {
log.Log(NET, "scanInterfaces() START")
ifaces, _ := net.Interfaces()
// me.ifnew = ifaces
log.Log(NET, SPEW, ifaces)
for _, i := range ifaces {
addrs, _ := i.Addrs()
// log.Info("range ifaces = ", i)
checkInterface(i)
log.Log(NET, "*net.Interface.Name = ", i.Name, i.Index)
log.Log(NET, SPEW, i)
log.Log(NET, SPEW, addrs)
for _, addr := range addrs {
log.Log(NET, "\taddr =", addr)
log.Log(NET, SPEW, addrs)
ips, _ := net.LookupIP(addr.String())
log.Log(NET, "\tLookupIP(addr) =", ips)
switch v := addr.(type) {
case *net.IPNet:
if checkIP(v, i) {
log.Log(true, "scanInterfaces() IP is new () i =", v.IP.String())
}
default:
log.Log(NET, "\t\taddr.(type) = NO IDEA WHAT TO DO HERE v =", v)
}
}
}
if deleteChanges() {
me.changed = true
log.Log(NET, "deleteChanges() detected network changes")
}
updateRealAAAA()
log.Log(NET, "scanInterfaces() END")
}
// displays the IP address found on your network interfaces
func updateRealAAAA() {
var all4 string
var all6 string
for s, t := range me.ipmap {
if (t.ipv4) {
all4 += s + "\n"
log.Log(NET, "IPv4 =", s)
} else if (t.ipv6) {
all6 += s + "\n"
log.Log(NET, "IPv6 =", s)
} else {
log.Log(NET, "???? =", s)
}
}
all4 = sortLines(all4)
all6 = sortLines(all6)
if (me.IPv4.Get() != all4) {
log.Log(NET, "IPv4 addresses have changed", all4)
me.IPv4.Set(all4)
}
if (me.IPv6.Get() != all6) {
log.Log(NET, "IPv6 addresses have changed", all6)
me.IPv6.Set(all6)
}
}
// delete network interfaces and ip addresses from the gui
func deleteChanges() bool {
var changed bool = false
for i, t := range me.ifmap {
if (t.gone) {
log.Log(CHANGE, "DELETE int =", i, "name =", t.name, t.iface)
delete(me.ifmap, i)
changed = true
}
t.gone = true
}
for s, t := range me.ipmap {
if (t.gone) {
log.Log(CHANGE, "DELETE name =", s, "IPv4 =", t.ipv4)
log.Log(CHANGE, "DELETE name =", s, "IPv6 =", t.ipv6)
log.Log(CHANGE, "DELETE name =", s, "iface =", t.iface)
log.Log(CHANGE, "DELETE name =", s, "ip =", t.ip)
delete(me.ipmap, s)
changed = true
}
t.gone = true
}
return changed
}

17
linuxstatus/new.go Normal file
View File

@ -0,0 +1,17 @@
// This creates a simple hello world window
package linuxstatus
import (
)
func New() *LinuxStatus {
me = &LinuxStatus {
hidden: true,
ready: false,
}
me.init = true
return me
// me.window = gadgets.NewBasicWindow(me.myGui, "Linux OS Details")
}

57
linuxstatus/structs.go Normal file
View File

@ -0,0 +1,57 @@
/*
figures out if your hostname is valid
then checks if your DNS is setup correctly
*/
package linuxstatus
import (
"net"
"go.wit.com/gui/gui"
"go.wit.com/gui/gadgets"
)
var me *LinuxStatus
type LinuxStatus struct {
init bool
ready bool
hidden bool
changed bool
ifmap map[int]*IFtype // the current interfaces
ipmap map[string]*IPtype // the current ip addresses
window *gadgets.BasicWindow
group *gui.Node
grid *gui.Node
hostshort *gadgets.OneLiner
domainname *gadgets.OneLiner
fqdn *gadgets.OneLiner
NSrr *gadgets.OneLiner
uid *gadgets.OneLiner
IPv4 *gadgets.OneLiner
IPv6 *gadgets.OneLiner
workingIPv6 *gadgets.OneLiner
Interfaces *gui.Node
speedActual *gadgets.OneLiner
}
type IPtype struct {
gone bool // used to track if the ip exists
ipv6 bool // the future
ipv4 bool // the past
LinkLocal bool
iface *net.Interface
ip net.IP
ipnet *net.IPNet
}
type IFtype struct {
gone bool // used to track if the interface exists
name string // just a shortcut to the name. maybe this is dumb
// up bool // could be used to track ifup/ifdown
iface *net.Interface
}

31
linuxstatus/timer.go Normal file
View File

@ -0,0 +1,31 @@
package linuxstatus
import (
"time"
"sort"
"strings"
)
// timeFunction takes a function as an argument and returns the execution time.
func timeFunction(f func()) time.Duration {
startTime := time.Now() // Record the start time
f() // Execute the function
return time.Since(startTime) // Calculate the elapsed time
}
// sortLines takes a string, splits it on newlines, sorts the lines,
// and rejoins them with newlines.
func sortLines(input string) string {
lines := strings.Split(input, "\n")
// Trim leading and trailing whitespace from each line
for i, line := range lines {
lines[i] = strings.TrimSpace(line)
}
sort.Strings(lines)
tmp := strings.Join(lines, "\n")
tmp = strings.TrimLeft(tmp, "\n")
tmp = strings.TrimRight(tmp, "\n")
return tmp
}

97
linuxstatus/unix.go Normal file
View File

@ -0,0 +1,97 @@
// Various Linux/Unix'y things
// https://wiki.archlinux.org/title/Dynamic_DNS
package linuxstatus
import (
"os"
"os/exec"
"net"
"bytes"
"fmt"
"strings"
"go.wit.com/log"
"go.wit.com/shell"
)
func CheckSuperuser() bool {
return os.Getuid() == 0
}
func Escalate() {
if os.Getuid() != 0 {
cmd := exec.Command("sudo", "./control-panel-dns") // TODO: get the actual path
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
log.Error(err, "exit in Escalate()")
log.Exit(err)
}
}
}
// You need permission to do a zone transfer. Otherwise:
// dig +noall +answer +multiline lab.wit.com any
// dig +all +multiline fire.lab.wit.com # gives the zonefile header (ttl vals)
func DumpPublicDNSZone(zone string) {
entries, err := net.LookupHost(zone)
if err != nil {
panic(err)
}
for _, entry := range entries {
log.Println(entry)
}
}
func dumpIPs(host string) {
ips, err := net.LookupIP(host)
if err != nil {
log.Error(err, "dumpIPs() failed")
}
for _, ip := range ips {
log.Println(host, ip)
}
}
/*
check if ddclient is installed, working, and/or configured
https://github.com/ddclient/ddclient
*/
func ddclient() {
}
/*
check if ddupdate is installed, working, and/or configured
*/
func ddupdate() {
}
func run(s string) string {
cmdArgs := strings.Fields(s)
// Define the command you want to run
// cmd := exec.Command(cmdArgs)
cmd := exec.Command(cmdArgs[0], cmdArgs[1:len(cmdArgs)]...)
// Create a buffer to capture the output
var out bytes.Buffer
// Set the output of the command to the buffer
cmd.Stdout = &out
// Run the command
err := cmd.Run()
if err != nil {
fmt.Println("Error running command:", err)
return ""
}
tmp := shell.Chomp(out.String())
// Output the results
log.Info("Command Output:", tmp)
return tmp
}

31
linuxstatus/update.go Normal file
View File

@ -0,0 +1,31 @@
package linuxstatus
import (
"errors"
"fmt"
"time"
"go.wit.com/log"
)
func (ls *LinuxStatus) Update() {
log.Info("linuxStatus() Update() START")
if ls == nil {
log.Error(errors.New("linuxStatus() Update() ls == nil"))
return
}
duration := timeFunction(func () {
linuxLoop()
})
s := fmt.Sprint(duration)
ls.speedActual.Set(s)
if (duration > 500 * time.Millisecond ) {
// ls.speed, "SLOW")
} else if (duration > 100 * time.Millisecond ) {
// ls.speed, "OK")
} else {
// ls.speed, "FAST")
}
log.Info("linuxStatus() Update() END")
}