2023-08-16 10:25:41 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2023-08-18 02:01:35 +00:00
|
|
|
"bufio"
|
2023-08-16 10:25:41 +00:00
|
|
|
"bytes"
|
|
|
|
_ "embed"
|
2023-08-18 02:01:35 +00:00
|
|
|
"encoding/json"
|
2023-08-16 10:25:41 +00:00
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"os/signal"
|
|
|
|
"strings"
|
2023-08-18 02:01:35 +00:00
|
|
|
"sync"
|
2023-08-16 10:25:41 +00:00
|
|
|
"syscall"
|
|
|
|
"text/template"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
//go:embed index.html
|
|
|
|
var tmpl string
|
|
|
|
|
|
|
|
var report = template.Must(template.New("").Parse(tmpl))
|
|
|
|
var write_report chan bool
|
|
|
|
var interval = time.Second * 2
|
2023-08-18 02:01:35 +00:00
|
|
|
var wr sync.Mutex
|
2023-08-16 10:25:41 +00:00
|
|
|
|
|
|
|
type Network struct {
|
|
|
|
Name string
|
|
|
|
IP string
|
|
|
|
OnFail string
|
|
|
|
}
|
|
|
|
|
|
|
|
var Networks = []*Network{
|
|
|
|
{
|
|
|
|
Name: "DSL",
|
|
|
|
IP: "220.135.85.67",
|
|
|
|
OnFail: "nmcli connection up DSL",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "Asus",
|
|
|
|
IP: "192.168.50.48",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
write_report = make(chan bool, 1)
|
|
|
|
|
|
|
|
file, err := os.OpenFile("network.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.ModePerm)
|
|
|
|
if err != nil {
|
|
|
|
log.Panicln(err)
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
log.Println("starting network monitor")
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
|
|
|
|
perr := false
|
|
|
|
derr := false
|
|
|
|
|
|
|
|
for reducer := 0; ; reducer++ {
|
|
|
|
now := time.Now().Format("2006-01-02 15:04:05")
|
|
|
|
|
|
|
|
// Public
|
|
|
|
go func(print bool) {
|
|
|
|
_, err = net.LookupIP("google.com")
|
|
|
|
if err != nil {
|
|
|
|
if !perr {
|
|
|
|
perr = true
|
|
|
|
log.Println(err)
|
|
|
|
}
|
2023-08-18 02:01:35 +00:00
|
|
|
wr.Lock()
|
2023-08-16 10:25:41 +00:00
|
|
|
fmt.Fprintf(file, `{"x": "%s", "y": "%s", "name": "%s"},`, now, "failed", "Public")
|
|
|
|
fmt.Fprintf(file, "\n")
|
2023-08-18 02:01:35 +00:00
|
|
|
wr.Unlock()
|
2023-08-16 10:25:41 +00:00
|
|
|
} else {
|
|
|
|
perr = false
|
|
|
|
if print {
|
2023-08-18 02:01:35 +00:00
|
|
|
wr.Lock()
|
2023-08-16 10:25:41 +00:00
|
|
|
fmt.Fprintf(file, `{"x": "%s", "y": "%s", "name": "%s"},`, now, "ok", "Public")
|
|
|
|
fmt.Fprintf(file, "\n")
|
2023-08-18 02:01:35 +00:00
|
|
|
wr.Unlock()
|
2023-08-16 10:25:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}(reducer == 0)
|
|
|
|
|
|
|
|
// devices
|
|
|
|
go func(print bool) {
|
|
|
|
for i := range Networks {
|
|
|
|
_, err = GetOutboundIP(Networks[i].IP + ":0")
|
|
|
|
if err != nil {
|
|
|
|
if !derr {
|
|
|
|
derr = true
|
|
|
|
log.Println(err)
|
|
|
|
}
|
2023-08-18 02:01:35 +00:00
|
|
|
wr.Lock()
|
2023-08-16 10:25:41 +00:00
|
|
|
fmt.Fprintf(file, `{"x": "%s", "y": "%s", "name": "%s"},`, now, "failed", Networks[i].Name)
|
|
|
|
fmt.Fprintf(file, "\n")
|
2023-08-18 02:01:35 +00:00
|
|
|
wr.Unlock()
|
2023-08-16 10:25:41 +00:00
|
|
|
// fmt.Fprintf(file, "%s\t%s\t%s\n", now, Networks[i].Name, "failed")
|
|
|
|
} else {
|
|
|
|
derr = false
|
|
|
|
if print {
|
2023-08-18 02:01:35 +00:00
|
|
|
wr.Lock()
|
2023-08-16 10:25:41 +00:00
|
|
|
fmt.Fprintf(file, `{"x": "%s", "y": "%s", "name": "%s"},`, now, "ok", Networks[i].Name)
|
|
|
|
fmt.Fprintf(file, "\n")
|
2023-08-18 02:01:35 +00:00
|
|
|
wr.Unlock()
|
2023-08-16 10:25:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}(reducer == 0)
|
|
|
|
|
|
|
|
<-time.After(interval)
|
|
|
|
if reducer >= 9 {
|
|
|
|
reducer = -1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
for {
|
2023-08-18 02:01:35 +00:00
|
|
|
<-time.After(time.Minute * 5)
|
|
|
|
CleanLog()
|
2023-08-16 10:25:41 +00:00
|
|
|
WriteReport()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
http.ServeFile(w, r, strings.TrimPrefix(r.URL.Path, "/"))
|
|
|
|
})
|
2023-08-18 02:01:35 +00:00
|
|
|
port, ok := os.LookupEnv("PORT")
|
|
|
|
if !ok {
|
|
|
|
port = "1080"
|
|
|
|
}
|
|
|
|
if err := http.ListenAndServe("0.0.0.0:"+port, nil); err != nil {
|
2023-08-16 10:25:41 +00:00
|
|
|
log.Fatalln(err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
wait := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(wait, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
|
|
|
|
<-wait
|
2023-08-18 02:01:35 +00:00
|
|
|
|
|
|
|
CleanLog()
|
2023-08-16 10:25:41 +00:00
|
|
|
WriteReport()
|
|
|
|
}
|
|
|
|
|
|
|
|
func WriteReport() {
|
|
|
|
|
|
|
|
select {
|
|
|
|
case write_report <- true:
|
|
|
|
|
|
|
|
default:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer func() { <-write_report }()
|
|
|
|
|
|
|
|
f, err := os.OpenFile("report.html", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
2023-08-18 02:01:35 +00:00
|
|
|
wr.Lock()
|
2023-08-16 10:25:41 +00:00
|
|
|
data, err := os.ReadFile("network.log")
|
2023-08-18 02:01:35 +00:00
|
|
|
wr.Unlock()
|
2023-08-16 10:25:41 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
content := &bytes.Buffer{}
|
|
|
|
if err = report.Execute(content, map[string]string{"Data": string(data)}); err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
content.WriteTo(f)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-08-18 02:01:35 +00:00
|
|
|
type point struct {
|
|
|
|
Time string `json:"x"`
|
|
|
|
Stat string `json:"y"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p point) GetTime() time.Time {
|
|
|
|
t, _ := time.Parse("2006-01-02 15:04:05", p.Time)
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
|
|
|
func CleanLog() error {
|
|
|
|
|
|
|
|
file, err := os.OpenFile("network.log", os.O_RDONLY|os.O_CREATE, os.ModePerm)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
last := map[string]*point{}
|
|
|
|
end := map[string]*point{}
|
|
|
|
queue := []*point{}
|
|
|
|
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
|
|
for scanner.Scan() {
|
|
|
|
pt := &point{}
|
|
|
|
data := scanner.Bytes()
|
|
|
|
err = json.Unmarshal(data[:len(data)-1], pt)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
file.Close()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
end[pt.Name] = pt
|
|
|
|
|
|
|
|
last_pt, ok := last[pt.Name]
|
|
|
|
if !ok {
|
|
|
|
last[pt.Name] = pt
|
|
|
|
queue = append(queue, pt)
|
|
|
|
} else {
|
|
|
|
if last_pt.Stat != pt.Stat {
|
|
|
|
last[pt.Name] = pt
|
|
|
|
queue = append(queue, pt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
file.Close()
|
|
|
|
|
|
|
|
for k, v := range end {
|
|
|
|
if pt, ok := last[k]; ok {
|
|
|
|
if pt.Time != v.Time {
|
|
|
|
queue = append(queue, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
wr.Lock()
|
|
|
|
defer wr.Unlock()
|
|
|
|
file, err = os.OpenFile("network.log", os.O_WRONLY|os.O_TRUNC, os.ModePerm)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
for i := range queue {
|
|
|
|
b, err := json.Marshal(queue[i])
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
file.Write(b)
|
|
|
|
file.Write([]byte{',', '\n'})
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-08-16 10:25:41 +00:00
|
|
|
// Get preferred outbound ip of this machine
|
|
|
|
func GetOutboundIP(addr string) (net.IP, error) {
|
|
|
|
|
|
|
|
iface, err := net.ResolveUDPAddr("udp", addr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
dialer := &net.Dialer{LocalAddr: iface}
|
|
|
|
|
|
|
|
conn, err := dialer.Dial("udp", "8.8.8.8:80")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer conn.Close()
|
|
|
|
|
|
|
|
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
|
|
|
|
|
|
|
return localAddr.IP, nil
|
|
|
|
}
|