├── .gitignore ├── LICENSE ├── README.md ├── config.yml ├── images ├── preview1.png └── preview2.png ├── linux_install.sh ├── src ├── config.go ├── cpu.go ├── disk.go ├── handlers.go ├── host.go ├── main.go ├── network │ ├── linux_network.go │ └── win_network.go ├── network_bandwidth.go ├── network_devices.go ├── process.go └── ram.go └── win_install.bat /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | main -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2019 Juan Huttemann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-server-monitor-api 2 | 3 | Gathers and Publish information about the Hardware, OS, Network and Processes of the current running machine through a JSON API (Linux & Windows Compatible) 4 | 5 | ![preview](https://raw.githubusercontent.com/juanhuttemann/go-server-monitor-api/master/images/preview1.png) 6 | ![preview](https://raw.githubusercontent.com/juanhuttemann/go-server-monitor-api/master/images/preview2.png) 7 | 8 | 9 | #### Clone 10 | 11 | ```sh 12 | git clone git@github.com:juanhuttemann/go-server-monitor-api.git 13 | ``` 14 | 15 | #### Dependencies 16 | 17 | ```sh 18 | go get github.com/shirou/gopsutil/cpu 19 | go get github.com/shirou/gopsutil/disk 20 | go get github.com/shirou/gopsutil/host 21 | go get github.com/shirou/gopsutil/mem 22 | go get github.com/mitchellh/go-ps 23 | go get github.com/spf13/viper 24 | ``` 25 | 26 | #### Build 27 | 28 | ```sh 29 | go build -o gomonitor src/*.go 30 | ``` 31 | 32 | #### Usage 33 | 34 | ```sh 35 | ./gomonitor 36 | ``` 37 | 38 | Then go to your web browser at [http:\//localhost:3000](http://localhost:3000) 39 | 40 | 41 | 42 | ### JSON Data References 43 | 44 | * **hostInfo** (object) 45 | * name (string): Host name 46 | * os (string): Operative System 47 | * arch (string): x86/x64 48 | * platform (string): OS Name, Version and Build 49 | * uptime (integer): amount of seconds since bootup 50 | 51 | * **cpu** (object) 52 | * name (string): Brand - Model of the Physical CPU 53 | * total (integer): Amount of Physical Cores 54 | * usage (float): Percentage of Physical CPU Usage 55 | * usagePerCore (Array): Array of Percentage of Logical CPU Usage 56 | 57 | * **ram** (object) 58 | * free (integer): Available Physical RAM in bytes 59 | * total (integer): Total of Physical RAM in bytes 60 | * usage (integer): Usage of Physical RAM in bytes 61 | 62 | * **disks** (array of objects) 63 | * mountPoint (string): Disk current mount path or Unit in Windows 64 | * size (integer): Total Disk space in bytes 65 | * used (integer): Used Disk space in bytes 66 | * percent (integer): Percentage of Disk Usage 67 | 68 | * **networkDevices** (array of objects) 69 | * name (string): Name of the Network Interface 70 | * addresses (array of objects): 71 | * cidr (string): Classless Inter-Domain Routing 72 | * ip (string): IP Address 73 | * mac (string): Mac Address 74 | * up (boolean): If the Net Interface is Up or Down 75 | 76 | 77 | ### Custom Settings 78 | 79 | 80 | #### Webserver API Port 81 | 82 | By default, the embedded server start on port 3000. We can provide a different value in the config.yml file: 83 | 84 | ``` 85 | # api server port 86 | port: 45000 87 | ``` 88 | 89 | #### Modules 90 | 91 | For enabling/disabling modules just edit the config.yml file changing the values by **true** or **false** 92 | 93 | ``` 94 | #available modules 95 | hostInfo: true 96 | cpu: true 97 | ram: true 98 | disks: true 99 | networkDevices: false 100 | networkBandwidth: true 101 | processes: false 102 | ``` 103 | 104 | 105 | -------------------------------------------------------------------------------- /config.yml: -------------------------------------------------------------------------------- 1 | # api server port 2 | port: 3000 3 | 4 | #available modules 5 | hostInfo: true 6 | cpu: true 7 | ram: true 8 | disks: true 9 | networkDevices: true 10 | networkBandwidth: true 11 | processes: true -------------------------------------------------------------------------------- /images/preview1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanhuttemann/go-server-monitor-api/09a84126306ae5af4c41653fa2b393a4f5409365/images/preview1.png -------------------------------------------------------------------------------- /images/preview2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juanhuttemann/go-server-monitor-api/09a84126306ae5af4c41653fa2b393a4f5409365/images/preview2.png -------------------------------------------------------------------------------- /linux_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | go get github.com/mitchellh/go-ps 3 | go get github.com/shirou/gopsutil/cpu 4 | go get github.com/shirou/gopsutil/disk 5 | go get github.com/shirou/gopsutil/host 6 | go get github.com/shirou/gopsutil/mem 7 | go get github.com/spf13/viper -------------------------------------------------------------------------------- /src/config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "reflect" 8 | "strconv" 9 | 10 | "github.com/spf13/viper" 11 | ) 12 | 13 | func init() { 14 | runPath, err := os.Getwd() 15 | if err != nil { 16 | log.Fatal(err) 17 | } 18 | if _, err := os.Stat(runPath + "/" + "config.yml"); os.IsNotExist(err) { 19 | fmt.Println("Config.yml doesn't exists") 20 | fmt.Println("Creating config.yml with default values") 21 | f, err := os.Create("config.yml") 22 | if err != nil { 23 | fmt.Println(err) 24 | return 25 | } 26 | _, err = f.WriteString(`# api server port 27 | port: 3000 28 | 29 | #available modules 30 | hostInfo: true 31 | cpu: true 32 | ram: true 33 | disks: true 34 | networkDevices: true 35 | networkBandwidth: true 36 | processes: true`) 37 | 38 | if err != nil { 39 | fmt.Println(err) 40 | f.Close() 41 | return 42 | } 43 | f.Close() 44 | } 45 | viper.SetConfigName("config") 46 | viper.SetConfigType("yaml") 47 | viper.AddConfigPath(runPath) 48 | err = viper.ReadInConfig() 49 | if err != nil { 50 | panic("config file error") 51 | } 52 | } 53 | 54 | func available(module string) bool { 55 | config := viper.Get(module) 56 | if config != nil { 57 | if reflect.TypeOf(config).String() == "bool" { 58 | return config.(bool) 59 | } 60 | return false 61 | } 62 | return false 63 | } 64 | 65 | func setPort() string { 66 | config := viper.Get("port") 67 | if config != nil { 68 | if reflect.TypeOf(config).String() == "int" { 69 | return strconv.Itoa(config.(int)) 70 | } 71 | return "3000" 72 | } 73 | return "3000" 74 | } 75 | -------------------------------------------------------------------------------- /src/cpu.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os/exec" 7 | "regexp" 8 | "runtime" 9 | "strings" 10 | "time" 11 | 12 | "github.com/shirou/gopsutil/cpu" 13 | ) 14 | 15 | type CPU struct { 16 | Name string `json:"name"` 17 | Total int `json:"total"` 18 | Usage float64 `json:"usage"` 19 | UsagePerCore []float64 `json:"usagePerCore"` 20 | } 21 | 22 | func cpuUsageGeneral(c chan float64) { 23 | duration := 500 * time.Millisecond 24 | cpuUsage, err := cpu.Percent(duration, false) 25 | if err != nil { 26 | panic(err) 27 | } 28 | c <- cpuUsage[0] 29 | } 30 | 31 | func cpuUsagePerCore(c chan []float64) { 32 | duration := 500 * time.Millisecond 33 | cpuUsage, err := cpu.Percent(duration, true) 34 | if err != nil { 35 | panic(err) 36 | } 37 | c <- cpuUsage 38 | } 39 | 40 | func CheckCPU() CPU { 41 | if !available("cpu") { 42 | return CPU{} 43 | } 44 | cpuChan := make(chan CPU) 45 | 46 | go func(c chan CPU) { 47 | cpuUsageGeneralChan := make(chan float64) 48 | cpuUsagePerCoreChan := make(chan []float64) 49 | 50 | go cpuUsageGeneral(cpuUsageGeneralChan) 51 | go cpuUsagePerCore(cpuUsagePerCoreChan) 52 | 53 | var cpuName string 54 | if runtime.GOOS == "windows" { 55 | out, err := exec.Command("wmic", "cpu", "get", "name").Output() 56 | if err != nil { 57 | log.Fatal(err) 58 | } 59 | 60 | cpuName = strings.TrimSpace(strings.Trim(string(out), "Name")) 61 | } else { 62 | command := []string{"/proc/cpuinfo"} 63 | out, err := exec.Command("cat", command...).Output() 64 | if err != nil { 65 | fmt.Println("an error has occurred while checking the cpu") 66 | log.Fatal(err) 67 | } 68 | 69 | re := regexp.MustCompile(`.*model name.*`) 70 | matches := re.FindStringSubmatch(string(out)) 71 | 72 | cpuName = strings.TrimSpace(strings.Trim(strings.Join(matches, " "), "model name")) 73 | cpuName = strings.Trim(cpuName, " :") 74 | } 75 | 76 | c <- CPU{ 77 | Total: runtime.NumCPU(), 78 | Name: cpuName, 79 | Usage: <-cpuUsageGeneralChan, 80 | UsagePerCore: <-cpuUsagePerCoreChan, 81 | } 82 | 83 | }(cpuChan) 84 | 85 | return <-cpuChan 86 | } 87 | -------------------------------------------------------------------------------- /src/disk.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/shirou/gopsutil/disk" 4 | 5 | //Disk properties 6 | type Disk struct { 7 | Mountpoint string `json:"mountPoint"` 8 | Free uint64 `json:"free"` 9 | Size uint64 `json:"size"` 10 | Used uint64 `json:"used"` 11 | Percent float64 `json:"percent"` 12 | } 13 | 14 | type Disks []Disk 15 | 16 | func CheckDisks() Disks { 17 | if !available("disks") { 18 | return Disks{} 19 | } 20 | disksChan := make(chan Disks) 21 | go func(c chan Disks) { 22 | disks, _ := disk.Partitions(false) 23 | 24 | var totalDisks Disks 25 | 26 | diskChan := make(chan Disk) 27 | for _, d := range disks { 28 | go func(d disk.PartitionStat) { 29 | diskUsageOf, _ := disk.Usage(d.Mountpoint) 30 | diskChan <- Disk{ 31 | Free: diskUsageOf.Free, 32 | Mountpoint: d.Mountpoint, 33 | Percent: diskUsageOf.UsedPercent, 34 | Size: diskUsageOf.Total, 35 | Used: diskUsageOf.Used, 36 | } 37 | 38 | }(d) 39 | 40 | } 41 | taskList := len(disks) 42 | 43 | for d := range diskChan { 44 | totalDisks = append(totalDisks, d) 45 | taskList-- 46 | if taskList == 0 { 47 | break 48 | } 49 | } 50 | c <- totalDisks 51 | }(disksChan) 52 | 53 | return <-disksChan 54 | } 55 | -------------------------------------------------------------------------------- /src/handlers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | ) 7 | 8 | type data struct { 9 | HostInfo HostInfo `json:"hostInfo"` 10 | CPU CPU `json:"cpu"` 11 | RAM RAM `json:"ram"` 12 | Disks []Disk `json:"disks"` 13 | NetworkDevices []NetworkDevice `json:"networkDevices"` 14 | NetworkBandwidth []NetworkDeviceBandwidth `json:"networkBandwidth"` 15 | Processes []Process `json:"processes"` 16 | } 17 | 18 | func ignoreFavicon(w http.ResponseWriter, r *http.Request) { 19 | w.WriteHeader(204) 20 | } 21 | 22 | func enableCors(w *http.ResponseWriter) { 23 | (*w).Header().Set("Access-Control-Allow-Origin", "*") 24 | } 25 | 26 | func checkData(w http.ResponseWriter, r *http.Request) { 27 | enableCors(&w) 28 | 29 | data := data{ 30 | HostInfo: CheckHostInfo(), 31 | RAM: CheckRAM(), 32 | CPU: CheckCPU(), 33 | NetworkDevices: CheckNetworkDevices(), 34 | NetworkBandwidth: CheckNetworkBandwidth(), 35 | Disks: CheckDisks(), 36 | Processes: CheckProcesses(), 37 | } 38 | 39 | js, err := json.Marshal(data) 40 | if err != nil { 41 | http.Error(w, err.Error(), http.StatusInternalServerError) 42 | return 43 | } 44 | 45 | w.Header().Set("Content-Type", "application/json") 46 | w.Write(js) 47 | } 48 | 49 | func CreateEndPoint(module string, e interface{}) { 50 | if available(module) { 51 | endPoint := func(w http.ResponseWriter, r *http.Request) { 52 | enableCors(&w) 53 | data := e 54 | js, err := json.Marshal(data) 55 | if err != nil { 56 | http.Error(w, err.Error(), http.StatusInternalServerError) 57 | return 58 | } 59 | 60 | w.Header().Set("Content-Type", "application/json") 61 | w.Write(js) 62 | } 63 | 64 | http.HandleFunc("/"+module, endPoint) 65 | } else { 66 | endPoint := func(w http.ResponseWriter, r *http.Request) { 67 | w.WriteHeader(http.StatusInternalServerError) 68 | w.Write([]byte("Error 500 - set " + module + " to 'true' in config.yml file")) 69 | } 70 | http.HandleFunc("/"+module, endPoint) 71 | } 72 | } 73 | 74 | func init() { 75 | http.HandleFunc("/", checkData) 76 | http.HandleFunc("/favicon.ico", ignoreFavicon) 77 | http.HandleFunc("/host", hostIndex) 78 | http.HandleFunc("/cpu", cpuIndex) 79 | http.HandleFunc("/ram", ramIndex) 80 | http.HandleFunc("/disks", disksIndex) 81 | http.HandleFunc("/networks", networkIndex) 82 | http.HandleFunc("/bandwidth", bandwidthIndex) 83 | http.HandleFunc("/processes", processesIndex) 84 | } 85 | 86 | func moduleServer(w http.ResponseWriter, checker interface{}, module string) { 87 | enableCors(&w) 88 | if available(module) { 89 | data := checker 90 | js, err := json.Marshal(data) 91 | if err != nil { 92 | http.Error(w, err.Error(), http.StatusInternalServerError) 93 | return 94 | } 95 | 96 | w.Header().Set("Content-Type", "application/json") 97 | w.Write(js) 98 | } else { 99 | w.WriteHeader(http.StatusInternalServerError) 100 | w.Write([]byte("Error 500 - set " + module + " to 'true' in config.yml file")) 101 | } 102 | } 103 | 104 | func hostIndex(w http.ResponseWriter, r *http.Request) { 105 | moduleServer(w, CheckHostInfo(), "hostInfo") 106 | } 107 | 108 | func ramIndex(w http.ResponseWriter, r *http.Request) { 109 | moduleServer(w, CheckRAM(), "ram") 110 | } 111 | func cpuIndex(w http.ResponseWriter, r *http.Request) { 112 | moduleServer(w, CheckCPU(), "cpu") 113 | } 114 | 115 | func disksIndex(w http.ResponseWriter, r *http.Request) { 116 | moduleServer(w, CheckDisks(), "disks") 117 | } 118 | 119 | func networkIndex(w http.ResponseWriter, r *http.Request) { 120 | moduleServer(w, CheckDisks(), "networkDevices") 121 | } 122 | 123 | func bandwidthIndex(w http.ResponseWriter, r *http.Request) { 124 | moduleServer(w, CheckNetworkBandwidth(), "networkBandwidth") 125 | } 126 | 127 | func processesIndex(w http.ResponseWriter, r *http.Request) { 128 | moduleServer(w, CheckProcesses(), "processes") 129 | } 130 | -------------------------------------------------------------------------------- /src/host.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | 7 | "github.com/shirou/gopsutil/host" 8 | ) 9 | 10 | //HostInfo machine data 11 | type HostInfo struct { 12 | Name string `json:"name"` 13 | OS string `json:"os"` 14 | Arch string `json:"arch"` 15 | Platform string `json:"platform"` 16 | Uptime uint64 `json:"uptime"` 17 | } 18 | 19 | func arch() string { 20 | archChan := make(chan string) 21 | go func(c chan string) { 22 | c <- runtime.GOARCH 23 | }(archChan) 24 | return <-archChan 25 | } 26 | 27 | func CheckHostInfo() HostInfo { 28 | if !available("hostInfo") { 29 | return HostInfo{} 30 | } 31 | 32 | hostInfoChan := make(chan HostInfo) 33 | go func(c chan HostInfo) { 34 | hostInfo, err := host.Info() 35 | if err != nil { 36 | fmt.Print(err) 37 | } 38 | 39 | c <- HostInfo{ 40 | Name: hostInfo.Hostname, 41 | OS: hostInfo.OS, 42 | Arch: arch(), 43 | Platform: hostInfo.Platform + " " + hostInfo.PlatformVersion, 44 | Uptime: hostInfo.Uptime, 45 | } 46 | }(hostInfoChan) 47 | return <-hostInfoChan 48 | } 49 | -------------------------------------------------------------------------------- /src/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/http" 7 | "strconv" 8 | ) 9 | 10 | var availableConfigPort string 11 | 12 | func portAvailable(configPort string) { 13 | fmt.Println("Setting port to " + configPort) 14 | ln, err := net.Listen("tcp", ":"+configPort) 15 | if err != nil { 16 | fmt.Println("Can't listen on port " + configPort) 17 | newConfigPort, errStrConv := strconv.Atoi(configPort) 18 | if errStrConv != nil { 19 | panic(errStrConv) 20 | } 21 | 22 | newConfigPort = newConfigPort + 1 23 | newConfigPortString := strconv.Itoa(newConfigPort) 24 | portAvailable(newConfigPortString) 25 | } else { 26 | _ = ln.Close() 27 | availableConfigPort = configPort 28 | } 29 | } 30 | 31 | func main() { 32 | configPort := setPort() //Read port from config.yml 33 | 34 | portAvailable(configPort) 35 | 36 | fmt.Println("Starting Server at port " + availableConfigPort) 37 | 38 | if err := http.ListenAndServe(":"+availableConfigPort, nil); err != nil { 39 | fmt.Println(err) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/network/linux_network.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package network 4 | 5 | import ( 6 | "bufio" 7 | "fmt" 8 | "io" 9 | "os" 10 | "strconv" 11 | "strings" 12 | ) 13 | 14 | //FIXME 15 | // AVOID LINUX COMPLAINING ABOUT SYSCALL 16 | type EthrNetDevStat struct { 17 | InterfaceName string 18 | RxBytes uint64 19 | TxBytes uint64 20 | RxPkts uint64 21 | TxPkts uint64 22 | } 23 | 24 | type EthrNetStat struct { 25 | NetDevStats []EthrNetDevStat 26 | } 27 | 28 | func GetNetworkStats() EthrNetStat { 29 | return EthrNetStat{} 30 | } 31 | 32 | //FIXME 33 | // AVOID LINUX COMPLAINING ABOUT SYSCALL 34 | 35 | func Get() ([]Stats, error) { 36 | file, err := os.Open("/proc/net/dev") 37 | if err != nil { 38 | return nil, err 39 | } 40 | defer file.Close() 41 | return collectNetworkStats(file) 42 | } 43 | 44 | type Stats struct { 45 | Name string 46 | RxBytes, TxBytes uint64 47 | } 48 | 49 | func collectNetworkStats(out io.Reader) ([]Stats, error) { 50 | scanner := bufio.NewScanner(out) 51 | var networks []Stats 52 | for scanner.Scan() { 53 | kv := strings.SplitN(scanner.Text(), ":", 2) 54 | if len(kv) != 2 { 55 | continue 56 | } 57 | fields := strings.Fields(kv[1]) 58 | if len(fields) < 16 { 59 | continue 60 | } 61 | name := strings.TrimSpace(kv[0]) 62 | if name == "lo" { 63 | continue 64 | } 65 | rxBytes, err := strconv.ParseUint(fields[0], 10, 64) 66 | if err != nil { 67 | return nil, fmt.Errorf("failed to parse rxBytes of %s", name) 68 | } 69 | txBytes, err := strconv.ParseUint(fields[8], 10, 64) 70 | if err != nil { 71 | return nil, fmt.Errorf("failed to parse txBytes of %s", name) 72 | } 73 | networks = append(networks, Stats{Name: name, RxBytes: rxBytes, TxBytes: txBytes}) 74 | } 75 | if err := scanner.Err(); err != nil { 76 | return nil, fmt.Errorf("scan error for /proc/net/dev: %s", err) 77 | } 78 | return networks, nil 79 | } 80 | -------------------------------------------------------------------------------- /src/network/win_network.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package network 4 | 5 | import ( 6 | "net" 7 | "sort" 8 | "strings" 9 | "syscall" 10 | "time" 11 | "unsafe" 12 | ) 13 | 14 | const ( 15 | MaxStringSize = 256 16 | MaxPhysicalAddrLength = 32 17 | pad0for64_4for32 = 0 18 | ) 19 | 20 | var kernel32 = syscall.NewLazyDLL("kernel32.dll") 21 | var user32 = syscall.NewLazyDLL("user32.dll") 22 | var iphlpapi = syscall.NewLazyDLL("iphlpapi.dll") 23 | var procGetIfEntry2 = iphlpapi.NewProc("GetIfEntry2") 24 | 25 | var statsEnabled bool 26 | var lastStatsTime = time.Now() 27 | 28 | type EthrNetStat struct { 29 | NetDevStats []EthrNetDevStat 30 | } 31 | 32 | type guid struct { 33 | Data1 uint32 34 | Data2 uint16 35 | Data3 uint16 36 | Data4 [8]byte 37 | } 38 | 39 | type mibIfRow2 struct { 40 | InterfaceLuid uint64 41 | InterfaceIndex uint32 42 | InterfaceGUID guid 43 | Alias [MaxStringSize + 1]uint16 44 | Description [MaxStringSize + 1]uint16 45 | PhysicalAddressLength uint32 46 | PhysicalAddress [MaxPhysicalAddrLength]uint8 47 | PermanentPhysicalAddress [MaxPhysicalAddrLength]uint8 48 | Mtu uint32 49 | Type uint32 50 | TunnelType uint32 51 | MediaType uint32 52 | PhysicalMediumType uint32 53 | AccessType uint32 54 | DirectionType uint32 55 | InterfaceAndOperStatusFlags uint32 56 | OperStatus uint32 57 | AdminStatus uint32 58 | MediaConnectState uint32 59 | NetworkGUID guid 60 | ConnectionType uint32 61 | padding1 [pad0for64_4for32]byte 62 | TransmitLinkSpeed uint64 63 | ReceiveLinkSpeed uint64 64 | InOctets uint64 65 | InUcastPkts uint64 66 | InNUcastPkts uint64 67 | InDiscards uint64 68 | InErrors uint64 69 | InUnknownProtos uint64 70 | InUcastOctets uint64 71 | InMulticastOctets uint64 72 | InBroadcastOctets uint64 73 | OutOctets uint64 74 | OutUcastPkts uint64 75 | OutNUcastPkts uint64 76 | OutDiscards uint64 77 | OutErrors uint64 78 | OutUcastOctets uint64 79 | OutMulticastOctets uint64 80 | OutBroadcastOctets uint64 81 | OutQLen uint64 82 | } 83 | 84 | type EthrNetDevStat struct { 85 | InterfaceName string 86 | RxBytes uint64 87 | TxBytes uint64 88 | RxPkts uint64 89 | TxPkts uint64 90 | } 91 | 92 | type EthrNetDevInfo struct { 93 | Bytes uint64 94 | Packets uint64 95 | Drop uint64 96 | Errs uint64 97 | } 98 | 99 | //FIXME 100 | //AVOID WINDOWS COMPLAINING 101 | type Stats struct { 102 | Name string 103 | RxBytes, TxBytes uint64 104 | } 105 | 106 | func Get() ([]Stats, error) { 107 | return []Stats{}, nil 108 | } 109 | 110 | //FIXME 111 | //AVOID WINDOWS COMPLAINING 112 | 113 | func GetNetDevStats(stats *EthrNetStat) { 114 | ifs, err := net.Interfaces() 115 | if err != nil { 116 | panic(err) 117 | } 118 | 119 | for _, ifi := range ifs { 120 | if (ifi.Flags&net.FlagUp) == 0 || strings.Contains(ifi.Name, "Pseudo") { 121 | continue 122 | } 123 | row := mibIfRow2{InterfaceIndex: uint32(ifi.Index)} 124 | e := getIfEntry2(&row) 125 | if e != nil { 126 | panic(err) 127 | } 128 | rxInfo := EthrNetDevInfo{ 129 | Bytes: uint64(row.InOctets), 130 | Packets: uint64(row.InUcastPkts), 131 | Drop: uint64(row.InDiscards), 132 | Errs: uint64(row.InErrors), 133 | } 134 | txInfo := EthrNetDevInfo{ 135 | Bytes: uint64(row.OutOctets), 136 | Packets: uint64(row.OutUcastPkts), 137 | Drop: uint64(row.OutDiscards), 138 | Errs: uint64(row.OutErrors), 139 | } 140 | netStats := EthrNetDevStat{ 141 | InterfaceName: ifi.Name, 142 | RxBytes: rxInfo.Bytes, 143 | TxBytes: txInfo.Bytes, 144 | RxPkts: rxInfo.Packets, 145 | TxPkts: txInfo.Packets, 146 | } 147 | stats.NetDevStats = append(stats.NetDevStats, netStats) 148 | } 149 | } 150 | 151 | func getIfEntry2(row *mibIfRow2) (errcode error) { 152 | r0, _, _ := syscall.Syscall(procGetIfEntry2.Addr(), 1, 153 | uintptr(unsafe.Pointer(row)), 0, 0) 154 | if r0 != 0 { 155 | errcode = syscall.Errno(r0) 156 | } 157 | return 158 | } 159 | 160 | func GetNetworkStats() EthrNetStat { 161 | stats := &EthrNetStat{} 162 | 163 | GetNetDevStats(stats) 164 | sort.SliceStable(stats.NetDevStats, func(i, j int) bool { 165 | return stats.NetDevStats[i].InterfaceName < stats.NetDevStats[j].InterfaceName 166 | }) 167 | 168 | return *stats 169 | } 170 | 171 | func getNetDevStatDiff(curStats EthrNetDevStat, prevNetStats EthrNetStat, seconds uint64) EthrNetDevStat { 172 | for _, prevStats := range prevNetStats.NetDevStats { 173 | if prevStats.InterfaceName != curStats.InterfaceName { 174 | continue 175 | } 176 | 177 | if curStats.RxBytes >= prevStats.RxBytes { 178 | curStats.RxBytes -= prevStats.RxBytes 179 | } else { 180 | curStats.RxBytes += (^uint64(0) - prevStats.RxBytes) 181 | } 182 | 183 | if curStats.TxBytes >= prevStats.TxBytes { 184 | curStats.TxBytes -= prevStats.TxBytes 185 | } else { 186 | curStats.TxBytes += (^uint64(0) - prevStats.TxBytes) 187 | } 188 | 189 | if curStats.RxPkts >= prevStats.RxPkts { 190 | curStats.RxPkts -= prevStats.RxPkts 191 | } else { 192 | curStats.RxPkts += (^uint64(0) - prevStats.RxPkts) 193 | } 194 | 195 | if curStats.TxPkts >= prevStats.TxPkts { 196 | curStats.TxPkts -= prevStats.TxPkts 197 | } else { 198 | curStats.TxPkts += (^uint64(0) - prevStats.TxPkts) 199 | } 200 | 201 | break 202 | } 203 | curStats.RxBytes /= seconds 204 | curStats.TxBytes /= seconds 205 | curStats.RxPkts /= seconds 206 | curStats.TxPkts /= seconds 207 | return curStats 208 | } 209 | 210 | func timeToNextTick() time.Duration { 211 | nextTick := lastStatsTime.Add(time.Second) 212 | return time.Until(nextTick) 213 | } 214 | -------------------------------------------------------------------------------- /src/network_bandwidth.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "runtime" 5 | "time" 6 | 7 | network "./network" 8 | ) 9 | 10 | //NetworkDeviceBandwidth properties 11 | type NetworkDeviceBandwidth struct { 12 | Name string `json:"name"` 13 | Rx uint64 `json:"rx"` 14 | Tx uint64 `json:"tx"` 15 | } 16 | 17 | func CheckNetworkBandwidth() []NetworkDeviceBandwidth { 18 | if !available("networkBandwidth") { 19 | return []NetworkDeviceBandwidth{} 20 | } 21 | NetworkDeviceBandwidthChan := make(chan []NetworkDeviceBandwidth) 22 | 23 | go func(c chan []NetworkDeviceBandwidth) { 24 | 25 | if runtime.GOOS == "windows" { 26 | stats := network.GetNetworkStats() 27 | var networkDevices []NetworkDeviceBandwidth 28 | for _, dev := range stats.NetDevStats { 29 | n := NetworkDeviceBandwidth{ 30 | Name: dev.InterfaceName, 31 | Rx: dev.RxBytes, 32 | Tx: dev.TxBytes, 33 | } 34 | networkDevices = append(networkDevices, n) 35 | 36 | } 37 | c <- networkDevices 38 | } 39 | var networkDevices []NetworkDeviceBandwidth 40 | 41 | stat0, err := network.Get() 42 | if err != nil { 43 | panic(err) 44 | } 45 | time.Sleep(time.Second * 1) 46 | stat1, err := network.Get() 47 | if err != nil { 48 | panic(err) 49 | } 50 | 51 | for i, s := range stat0 { 52 | n := NetworkDeviceBandwidth{ 53 | Name: s.Name, 54 | Rx: stat1[i].RxBytes - s.RxBytes, 55 | Tx: stat1[i].TxBytes - s.TxBytes, 56 | } 57 | networkDevices = append(networkDevices, n) 58 | c <- networkDevices 59 | 60 | } 61 | }(NetworkDeviceBandwidthChan) 62 | return <-NetworkDeviceBandwidthChan 63 | } 64 | -------------------------------------------------------------------------------- /src/network_devices.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "strings" 7 | ) 8 | 9 | //Address of NetworkDevice 10 | type Address struct { 11 | IP string `json:"ip"` 12 | } 13 | 14 | type Addresses []Address 15 | 16 | //NetworkDevice properties 17 | type NetworkDevice struct { 18 | Name string `json:"name"` 19 | Addresses Addresses `json:"addresses"` 20 | MAC string `json:"mac"` 21 | UP bool `json:"up"` 22 | } 23 | 24 | type NetworkDevices []NetworkDevice 25 | 26 | func CheckNetworkDevices() NetworkDevices { 27 | if !available("networkDevices") { 28 | return NetworkDevices{} 29 | } 30 | networkDeviceChan := make(chan NetworkDevices) 31 | go func(c chan NetworkDevices) { 32 | var networkDevices NetworkDevices 33 | var mac string 34 | interfaces, err := net.Interfaces() 35 | 36 | if err != nil { 37 | fmt.Print(err) 38 | } 39 | 40 | netChan := make(chan NetworkDevice) 41 | for _, iface := range interfaces { 42 | go func(iface net.Interface) { 43 | byNameInterface, err := net.InterfaceByName(iface.Name) 44 | if err != nil { 45 | fmt.Println(err) 46 | } 47 | 48 | if iface.HardwareAddr.String() == "" { 49 | mac = "00:00:00:00:00:00" 50 | } else { 51 | mac = iface.HardwareAddr.String() 52 | } 53 | 54 | networkDevice := NetworkDevice{ 55 | Name: iface.Name, 56 | MAC: mac, 57 | } 58 | 59 | if (iface.Flags & net.FlagUp) == 0 { 60 | networkDevice.UP = false 61 | } else { 62 | networkDevice.UP = true 63 | } 64 | 65 | addresses, err := byNameInterface.Addrs() 66 | 67 | if err != nil { 68 | fmt.Println(err) 69 | } 70 | 71 | var addr Addresses 72 | 73 | for _, address := range addresses { 74 | addr = append(addr, Address{ 75 | IP: strings.Split(address.String(), "/")[0], 76 | }) 77 | } 78 | 79 | networkDevice.Addresses = addr 80 | 81 | netChan <- networkDevice 82 | }(iface) 83 | } 84 | taskList := len(interfaces) 85 | 86 | for i := range netChan { 87 | networkDevices = append(networkDevices, i) 88 | taskList-- 89 | if taskList == 0 { 90 | break 91 | } 92 | } 93 | c <- networkDevices 94 | }(networkDeviceChan) 95 | return <-networkDeviceChan 96 | } 97 | -------------------------------------------------------------------------------- /src/process.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/mitchellh/go-ps" 4 | 5 | //Process of the OS 6 | type Process struct { 7 | Pid int `json:"pid"` 8 | Name string `json:"name"` 9 | } 10 | 11 | type Processes []Process 12 | 13 | func CheckProcesses() Processes { 14 | if !available("processes") { 15 | return Processes{} 16 | } 17 | processChan := make(chan Processes) 18 | go func(c chan Processes) { 19 | processes, err := ps.Processes() 20 | if err != nil { 21 | panic(err) 22 | } 23 | var processList Processes 24 | procChan := make(chan Process) 25 | for _, p := range processes { 26 | go func(p ps.Process) { 27 | proc := Process{Pid: p.Pid(), Name: p.Executable()} 28 | procChan <- proc 29 | }(p) 30 | } 31 | 32 | taskList := len(processes) 33 | 34 | for p := range procChan { 35 | processList = append(processList, p) 36 | taskList-- 37 | if taskList == 0 { 38 | break 39 | } 40 | } 41 | c <- processList 42 | }(processChan) 43 | return <-processChan 44 | } 45 | -------------------------------------------------------------------------------- /src/ram.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/shirou/gopsutil/mem" 7 | ) 8 | 9 | //RAM details in host 10 | type RAM struct { 11 | Free uint64 `json:"free"` 12 | Total uint64 `json:"total"` 13 | Usage uint64 `json:"usage"` 14 | } 15 | 16 | func CheckRAM() RAM { 17 | if !available("ram") { 18 | return RAM{} 19 | } 20 | ramChan := make(chan RAM) 21 | go func(c chan RAM) { 22 | memory, err := mem.VirtualMemory() 23 | if err != nil { 24 | fmt.Print(err) 25 | } 26 | c <- RAM{ 27 | Free: memory.Total - memory.Used, 28 | Usage: memory.Used, 29 | Total: memory.Total, 30 | } 31 | }(ramChan) 32 | 33 | return <-ramChan 34 | } 35 | -------------------------------------------------------------------------------- /win_install.bat: -------------------------------------------------------------------------------- 1 | go get github.com/mitchellh/go-ps 2 | go get github.com/shirou/gopsutil/cpu 3 | go get github.com/shirou/gopsutil/disk 4 | go get github.com/shirou/gopsutil/host 5 | go get github.com/shirou/gopsutil/mem 6 | go get github.com/spf13/viper --------------------------------------------------------------------------------