update
						commit
						d51ccc456b
					
				|  | @ -0,0 +1,2 @@ | |||
| nwm | ||||
| runtime | ||||
|  | @ -0,0 +1,62 @@ | |||
| <div> | ||||
|   <canvas id="myChart"></canvas> | ||||
| </div> | ||||
| 
 | ||||
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | ||||
| 
 | ||||
| <script src="https://cdn.jsdelivr.net/npm/chart.js@^3"></script> | ||||
| <script src="https://cdn.jsdelivr.net/npm/moment@^2"></script> | ||||
| <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment@^1"></script> | ||||
| 
 | ||||
| <script> | ||||
|   const ctx = document.getElementById('myChart'); | ||||
| 
 | ||||
|   const fails = [ | ||||
|     {{.Data}} | ||||
|   ] | ||||
| 
 | ||||
|   new Chart(ctx, { | ||||
|     type: 'line', | ||||
|     data: { | ||||
|       datasets: [ | ||||
|         { | ||||
|           label: 'Public Network', | ||||
|           data: fails.filter(el=>el.name=='Public'), | ||||
|           borderColor: 'rgb(255, 99, 132)', | ||||
|           backgroundColor: 'rgb(255, 99, 132)', | ||||
|         }, | ||||
|         { | ||||
|           label: 'DSL', | ||||
|           data: fails.filter(el=>el.name=='DSL'), | ||||
|           borderColor: 'rgb(75, 192, 192)', | ||||
|           backgroundColor: 'rgb(75, 192, 192)' | ||||
|         }, | ||||
|         { | ||||
|           label: 'Asus Router', | ||||
|           data: fails.filter(el=>el.name=='Asus'), | ||||
|           borderColor: 'rgb(255, 205, 86)', | ||||
|           backgroundColor: 'rgb(255, 205, 86)' | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     options: { | ||||
|       scales: { | ||||
|         x: { | ||||
|           type: 'time', | ||||
|           time: { | ||||
|             unit: 'second' | ||||
|           }, | ||||
|           position: 'bottom' | ||||
|         }, | ||||
|         y: { | ||||
|           type: 'category', | ||||
|           labels: ['', 'ok','failed',''] | ||||
|         } | ||||
|       }, | ||||
|       decimation: { | ||||
|         enabled: true, | ||||
|         algorithm: 'lttb' | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| </script> | ||||
|  | @ -0,0 +1,184 @@ | |||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	_ "embed" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"os/signal" | ||||
| 	"strings" | ||||
| 	"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 | ||||
| 
 | ||||
| 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) | ||||
| 					} | ||||
| 					fmt.Fprintf(file, `{"x": "%s", "y": "%s", "name": "%s"},`, now, "failed", "Public") | ||||
| 					fmt.Fprintf(file, "\n") | ||||
| 				} else { | ||||
| 					perr = false | ||||
| 					if print { | ||||
| 						fmt.Fprintf(file, `{"x": "%s", "y": "%s", "name": "%s"},`, now, "ok", "Public") | ||||
| 						fmt.Fprintf(file, "\n") | ||||
| 					} | ||||
| 				} | ||||
| 			}(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) | ||||
| 						} | ||||
| 						fmt.Fprintf(file, `{"x": "%s", "y": "%s", "name": "%s"},`, now, "failed", Networks[i].Name) | ||||
| 						fmt.Fprintf(file, "\n") | ||||
| 						// fmt.Fprintf(file, "%s\t%s\t%s\n", now, Networks[i].Name, "failed")
 | ||||
| 					} else { | ||||
| 						derr = false | ||||
| 						if print { | ||||
| 							fmt.Fprintf(file, `{"x": "%s", "y": "%s", "name": "%s"},`, now, "ok", Networks[i].Name) | ||||
| 							fmt.Fprintf(file, "\n") | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			}(reducer == 0) | ||||
| 
 | ||||
| 			<-time.After(interval) | ||||
| 			if reducer >= 9 { | ||||
| 				reducer = -1 | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			<-time.After(time.Minute * 10) | ||||
| 			WriteReport() | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	go func() { | ||||
| 		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | ||||
| 			http.ServeFile(w, r, strings.TrimPrefix(r.URL.Path, "/")) | ||||
| 		}) | ||||
| 		if err := http.ListenAndServe("0.0.0.0:1080", nil); err != nil { | ||||
| 			log.Fatalln(err) | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	wait := make(chan os.Signal, 1) | ||||
| 	signal.Notify(wait, syscall.SIGINT, syscall.SIGTERM) | ||||
| 
 | ||||
| 	<-wait | ||||
| 	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() | ||||
| 
 | ||||
| 	data, err := os.ReadFile("network.log") | ||||
| 	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) | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| // 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 | ||||
| } | ||||
|  | @ -0,0 +1,13 @@ | |||
| [Unit] | ||||
| Description=Evan Network Monitor | ||||
| 
 | ||||
| [Service] | ||||
| WorkingDirectory=/home/evan/Documents/network-monitor/runtime | ||||
| ExecStart=/home/evan/Documents/network-monitor/nwm | ||||
| Restart=always | ||||
| 
 | ||||
| StandardOutput=append:/home/evan/Documents/network-monitor/runtime/service.log | ||||
| StandardError=append:/home/evan/Documents/network-monitor/runtime/service.log | ||||
| 
 | ||||
| [Install] | ||||
| WantedBy=multi-user.target | ||||
|  | @ -0,0 +1,7 @@ | |||
| 
 | ||||
| 
 | ||||
| sudo systemctl stop nwm | ||||
| 
 | ||||
| go build -o nwm main.go | ||||
| 
 | ||||
| sudo systemctl start nwm | ||||
		Loading…
	
		Reference in New Issue