├── .gitignore ├── README.md ├── cpuinfo.go ├── cpustat.go ├── dfstat.go ├── dfstat_darwin.go ├── dfstat_linux.go ├── env.go ├── env_test.go ├── ifstat.go ├── iostat.go ├── kernel.go ├── loadavg.go ├── main_test.go ├── meminfo.go ├── netstat.go ├── portstat.go ├── proc.go ├── snmp.go ├── ss_s.go └── system.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | nux 2 | === 3 | 4 | *nux metric collector 5 | -------------------------------------------------------------------------------- /cpuinfo.go: -------------------------------------------------------------------------------- 1 | package nux 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "github.com/toolkits/file" 8 | "io" 9 | "io/ioutil" 10 | "runtime" 11 | "strings" 12 | ) 13 | 14 | func NumCpu() int { 15 | return runtime.NumCPU() 16 | } 17 | 18 | func CpuMHz() (mhz string, err error) { 19 | f := "/proc/cpuinfo" 20 | var bs []byte 21 | bs, err = ioutil.ReadFile(f) 22 | if err != nil { 23 | return 24 | } 25 | 26 | reader := bufio.NewReader(bytes.NewBuffer(bs)) 27 | 28 | for { 29 | var lineBytes []byte 30 | lineBytes, err = file.ReadLine(reader) 31 | if err == io.EOF { 32 | return 33 | } 34 | 35 | line := string(lineBytes) 36 | if !strings.Contains(line, "MHz") { 37 | continue 38 | } 39 | 40 | arr := strings.Split(line, ":") 41 | if len(arr) != 2 { 42 | return "", fmt.Errorf("%s content format error", f) 43 | } 44 | 45 | return strings.TrimSpace(arr[1]), nil 46 | } 47 | 48 | return "", fmt.Errorf("no MHz in %s", f) 49 | } 50 | -------------------------------------------------------------------------------- /cpustat.go: -------------------------------------------------------------------------------- 1 | package nux 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "strconv" 10 | "strings" 11 | 12 | "github.com/toolkits/file" 13 | ) 14 | 15 | type CpuUsage struct { 16 | User uint64 // time spent in user mode 17 | Nice uint64 // time spent in user mode with low priority (nice) 18 | System uint64 // time spent in system mode 19 | Idle uint64 // time spent in the idle task 20 | Iowait uint64 // time spent waiting for I/O to complete (since Linux 2.5.41) 21 | Irq uint64 // time spent servicing interrupts (since 2.6.0-test4) 22 | SoftIrq uint64 // time spent servicing softirqs (since 2.6.0-test4) 23 | Steal uint64 // time spent in other OSes when running in a virtualized environment (since 2.6.11) 24 | Guest uint64 // time spent running a virtual CPU for guest operating systems under the control of the Linux kernel. (since 2.6.24) 25 | Total uint64 // total of all time fields 26 | } 27 | 28 | func (this *CpuUsage) String() string { 29 | return fmt.Sprintf("", 30 | this.User, 31 | this.Nice, 32 | this.System, 33 | this.Idle, 34 | this.Iowait, 35 | this.Irq, 36 | this.SoftIrq, 37 | this.Steal, 38 | this.Guest, 39 | this.Total) 40 | } 41 | 42 | type ProcStat struct { 43 | Cpu *CpuUsage 44 | Cpus []*CpuUsage 45 | Ctxt uint64 46 | Processes uint64 47 | ProcsRunning uint64 48 | ProcsBlocked uint64 49 | } 50 | 51 | func (this *ProcStat) String() string { 52 | return fmt.Sprintf("", 53 | this.Cpu, 54 | this.Cpus, 55 | this.Ctxt, 56 | this.Processes, 57 | this.ProcsRunning, 58 | this.ProcsBlocked) 59 | } 60 | 61 | func CurrentProcStat() (*ProcStat, error) { 62 | f := Root() + "/proc/stat" 63 | bs, err := ioutil.ReadFile(f) 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | ps := &ProcStat{Cpus: make([]*CpuUsage, NumCpu())} 69 | reader := bufio.NewReader(bytes.NewBuffer(bs)) 70 | 71 | for { 72 | line, err := file.ReadLine(reader) 73 | if err == io.EOF { 74 | err = nil 75 | break 76 | } else if err != nil { 77 | return ps, err 78 | } 79 | parseLine(line, ps) 80 | } 81 | 82 | return ps, nil 83 | } 84 | 85 | func parseLine(line []byte, ps *ProcStat) { 86 | fields := strings.Fields(string(line)) 87 | if len(fields) < 2 { 88 | return 89 | } 90 | 91 | fieldName := fields[0] 92 | if fieldName == "cpu" { 93 | ps.Cpu = parseCpuFields(fields) 94 | return 95 | } 96 | 97 | if strings.HasPrefix(fieldName, "cpu") { 98 | idx, err := strconv.Atoi(fieldName[3:]) 99 | if err != nil || idx >= len(ps.Cpus) { 100 | return 101 | } 102 | 103 | ps.Cpus[idx] = parseCpuFields(fields) 104 | return 105 | } 106 | 107 | if fieldName == "ctxt" { 108 | ps.Ctxt, _ = strconv.ParseUint(fields[1], 10, 64) 109 | return 110 | } 111 | 112 | if fieldName == "processes" { 113 | ps.Processes, _ = strconv.ParseUint(fields[1], 10, 64) 114 | return 115 | } 116 | 117 | if fieldName == "procs_running" { 118 | ps.ProcsRunning, _ = strconv.ParseUint(fields[1], 10, 64) 119 | return 120 | } 121 | 122 | if fieldName == "procs_blocked" { 123 | ps.ProcsBlocked, _ = strconv.ParseUint(fields[1], 10, 64) 124 | return 125 | } 126 | } 127 | 128 | func parseCpuFields(fields []string) *CpuUsage { 129 | cu := new(CpuUsage) 130 | sz := len(fields) 131 | for i := 1; i < sz; i++ { 132 | val, err := strconv.ParseUint(fields[i], 10, 64) 133 | if err != nil { 134 | continue 135 | } 136 | 137 | // user time is already include guest time 138 | if i != 9 { 139 | cu.Total += val 140 | } 141 | switch i { 142 | case 1: 143 | cu.User = val 144 | case 2: 145 | cu.Nice = val 146 | case 3: 147 | cu.System = val 148 | case 4: 149 | cu.Idle = val 150 | case 5: 151 | cu.Iowait = val 152 | case 6: 153 | cu.Irq = val 154 | case 7: 155 | cu.SoftIrq = val 156 | case 8: 157 | cu.Steal = val 158 | case 9: 159 | cu.Guest = val 160 | } 161 | } 162 | return cu 163 | } 164 | -------------------------------------------------------------------------------- /dfstat.go: -------------------------------------------------------------------------------- 1 | package nux 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | var FSSPEC_IGNORE = map[string]struct{}{ 9 | "none": struct{}{}, 10 | "nodev": struct{}{}, 11 | "proc": struct{}{}, 12 | "hugetlbfs": struct{}{}, 13 | "mqueue": struct{}{}, 14 | } 15 | 16 | var FSTYPE_IGNORE = map[string]struct{}{ 17 | "cgroup": struct{}{}, 18 | "debugfs": struct{}{}, 19 | "devpts": struct{}{}, 20 | "devtmpfs": struct{}{}, 21 | "iso9660": struct{}{}, 22 | "rpc_pipefs": struct{}{}, 23 | "rootfs": struct{}{}, 24 | "overlay": struct{}{}, 25 | "tmpfs": struct{}{}, 26 | "squashfs": struct{}{}, 27 | } 28 | 29 | var FSFILE_PREFIX_IGNORE = []string{ 30 | "/sys", 31 | "/net", 32 | "/misc", 33 | "/proc", 34 | "/lib", 35 | } 36 | 37 | func IgnoreFsFile(fs_file string) bool { 38 | for _, prefix := range FSFILE_PREFIX_IGNORE { 39 | if strings.HasPrefix(fs_file, prefix) { 40 | return true 41 | } 42 | } 43 | 44 | return false 45 | } 46 | 47 | type DeviceUsage struct { 48 | FsSpec string 49 | FsFile string 50 | FsVfstype string 51 | BlocksAll uint64 52 | BlocksUsed uint64 53 | BlocksFree uint64 54 | BlocksUsedPercent float64 55 | BlocksFreePercent float64 56 | InodesAll uint64 57 | InodesUsed uint64 58 | InodesFree uint64 59 | InodesUsedPercent float64 60 | InodesFreePercent float64 61 | } 62 | 63 | func (this *DeviceUsage) String() string { 64 | return fmt.Sprintf("", 65 | this.FsSpec, 66 | this.FsFile, 67 | this.FsVfstype, 68 | this.BlocksFreePercent) 69 | } 70 | -------------------------------------------------------------------------------- /dfstat_darwin.go: -------------------------------------------------------------------------------- 1 | package nux 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // return: [][$fs_spec, $fs_file, $fs_vfstype] 8 | func ListMountPoint() ([][3]string, error) { 9 | return [][3]string{}, nil 10 | } 11 | 12 | func BuildDeviceUsage(_fsSpec, _fsFile, _fsVfstype string) (*DeviceUsage, error) { 13 | return nil, fmt.Errorf("not supported") 14 | } 15 | -------------------------------------------------------------------------------- /dfstat_linux.go: -------------------------------------------------------------------------------- 1 | package nux 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "io" 7 | "io/ioutil" 8 | "math" 9 | "strings" 10 | "syscall" 11 | 12 | "github.com/toolkits/file" 13 | ) 14 | 15 | // return: [][$fsSpec, $fsFile, $fsVfstype] 16 | func ListMountPoint() ([][3]string, error) { 17 | contents, err := ioutil.ReadFile(Root() + "/proc/mounts") 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | ret := make([][3]string, 0) 23 | 24 | reader := bufio.NewReader(bytes.NewBuffer(contents)) 25 | for { 26 | line, err := file.ReadLine(reader) 27 | if err == io.EOF { 28 | err = nil 29 | break 30 | } else if err != nil { 31 | return nil, err 32 | } 33 | 34 | fields := strings.Fields(string(line)) 35 | // Docs come from the fstab(5) 36 | // fsSpec # Mounted block special device or remote filesystem e.g. /dev/sda1 37 | // fsFile # Mount point e.g. /data 38 | // fsVfstype # File system type e.g. ext4 39 | // fs_mntops # Mount options 40 | // fs_freq # Dump(8) utility flags 41 | // fs_passno # Order in which filesystem checks are done at reboot time 42 | 43 | fsSpec := fields[0] 44 | fsFile := fields[1] 45 | fsVfstype := fields[2] 46 | 47 | if _, exist := FSSPEC_IGNORE[fsSpec]; exist { 48 | continue 49 | } 50 | 51 | if _, exist := FSTYPE_IGNORE[fsVfstype]; exist { 52 | continue 53 | } 54 | 55 | if strings.HasPrefix(fsVfstype, "fuse") { 56 | continue 57 | } 58 | 59 | if IgnoreFsFile(fsFile) { 60 | continue 61 | } 62 | 63 | // keep /dev/xxx device with shorter fsFile (remove mount binds) 64 | if strings.HasPrefix(fsSpec, Root()+"/dev") { 65 | deviceFound := false 66 | for idx := range ret { 67 | if ret[idx][0] == fsSpec { 68 | deviceFound = true 69 | if len(fsFile) < len(ret[idx][1]) { 70 | ret[idx][1] = fsFile 71 | } 72 | break 73 | } 74 | } 75 | if !deviceFound { 76 | ret = append(ret, [3]string{fsSpec, fsFile, fsVfstype}) 77 | } 78 | } else { 79 | ret = append(ret, [3]string{fsSpec, fsFile, fsVfstype}) 80 | } 81 | } 82 | return ret, nil 83 | } 84 | 85 | func BuildDeviceUsage(_fsSpec, _fsFile, _fsVfstype string) (*DeviceUsage, error) { 86 | ret := &DeviceUsage{FsSpec: _fsSpec, FsFile: _fsFile, FsVfstype: _fsVfstype} 87 | 88 | fs := syscall.Statfs_t{} 89 | err := syscall.Statfs(_fsFile, &fs) 90 | if err != nil { 91 | return nil, err 92 | } 93 | 94 | // blocks 95 | used := fs.Blocks - fs.Bfree 96 | ret.BlocksAll = uint64(fs.Frsize) * fs.Blocks 97 | ret.BlocksUsed = uint64(fs.Frsize) * used 98 | ret.BlocksFree = uint64(fs.Frsize) * fs.Bavail 99 | if fs.Blocks == 0 { 100 | ret.BlocksUsedPercent = 0 101 | ret.BlocksFreePercent = 0 102 | } else { 103 | ret.BlocksUsedPercent = float64(used) * 100.0 / float64(used+fs.Bavail) 104 | ret.BlocksFreePercent = 100.0 - ret.BlocksUsedPercent 105 | } 106 | 107 | // inodes 108 | ret.InodesAll = fs.Files 109 | if fs.Ffree == math.MaxUint64 { 110 | ret.InodesFree = 0 111 | ret.InodesUsed = 0 112 | } else { 113 | ret.InodesFree = fs.Ffree 114 | ret.InodesUsed = fs.Files - fs.Ffree 115 | } 116 | if fs.Files == 0 { 117 | ret.InodesUsedPercent = 0 118 | ret.InodesFreePercent = 0 119 | } else { 120 | ret.InodesUsedPercent = float64(ret.InodesUsed) * 100.0 / float64(ret.InodesAll) 121 | ret.InodesFreePercent = 100.0 - ret.InodesUsedPercent 122 | } 123 | 124 | return ret, nil 125 | } 126 | -------------------------------------------------------------------------------- /env.go: -------------------------------------------------------------------------------- 1 | package nux 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | ) 7 | 8 | const nuxRootFs = "NUX_ROOTFS" 9 | 10 | // Root 获取系统变量 11 | func Root() string { 12 | root := os.Getenv(nuxRootFs) 13 | if !strings.HasPrefix(root, string(os.PathSeparator)) { 14 | return "" 15 | } 16 | root = strings.TrimSuffix(root, string(os.PathSeparator)) 17 | if pathExists(root) { 18 | return root 19 | } 20 | return "" 21 | } 22 | 23 | func pathExists(path string) bool { 24 | fi, err := os.Stat(path) 25 | if err == nil { 26 | return fi.IsDir() 27 | } 28 | return false 29 | } 30 | -------------------------------------------------------------------------------- /env_test.go: -------------------------------------------------------------------------------- 1 | package nux 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | func TestRoot(t *testing.T) { 9 | tests := []struct { 10 | name string 11 | want string 12 | }{ 13 | { 14 | want: "", 15 | }, 16 | } 17 | for _, tt := range tests { 18 | t.Run(tt.name, func(t *testing.T) { 19 | if got := Root(); got != tt.want { 20 | t.Errorf("Root() = %v, want %v", got, tt.want) 21 | } 22 | }) 23 | } 24 | } 25 | 26 | func TestRootWithValue(t *testing.T) { 27 | os.Setenv(nuxRootFs, "/var") 28 | tests := []struct { 29 | name string 30 | want string 31 | }{ 32 | { 33 | want: "/var", 34 | }, 35 | } 36 | for _, tt := range tests { 37 | t.Run(tt.name, func(t *testing.T) { 38 | if got := Root(); got != tt.want { 39 | t.Errorf("Root() = %v, want %v", got, tt.want) 40 | } 41 | }) 42 | } 43 | } 44 | 45 | func TestRootWithErrorFormatValue(t *testing.T) { 46 | os.Setenv(nuxRootFs, "/var:/tmp") 47 | tests := []struct { 48 | name string 49 | want string 50 | }{ 51 | { 52 | want: "", 53 | }, 54 | } 55 | for _, tt := range tests { 56 | t.Run(tt.name, func(t *testing.T) { 57 | if got := Root(); got != tt.want { 58 | t.Errorf("Root() = %v, want %v", got, tt.want) 59 | } 60 | }) 61 | } 62 | } 63 | 64 | func TestRootWithNotExistsValue(t *testing.T) { 65 | os.Setenv(nuxRootFs, "/var_NotExists") 66 | tests := []struct { 67 | name string 68 | want string 69 | }{ 70 | { 71 | want: "", 72 | }, 73 | } 74 | for _, tt := range tests { 75 | t.Run(tt.name, func(t *testing.T) { 76 | if got := Root(); got != tt.want { 77 | t.Errorf("Root() = %v, want %v", got, tt.want) 78 | } 79 | }) 80 | } 81 | } 82 | 83 | func TestRootWithRelativeValue(t *testing.T) { 84 | os.Setenv(nuxRootFs, "var_NotExists") 85 | tests := []struct { 86 | name string 87 | want string 88 | }{ 89 | { 90 | want: "", 91 | }, 92 | } 93 | for _, tt := range tests { 94 | t.Run(tt.name, func(t *testing.T) { 95 | if got := Root(); got != tt.want { 96 | t.Errorf("Root() = %v, want %v", got, tt.want) 97 | } 98 | }) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /ifstat.go: -------------------------------------------------------------------------------- 1 | package nux 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "strconv" 10 | "strings" 11 | 12 | "github.com/toolkits/file" 13 | "github.com/toolkits/sys" 14 | ) 15 | 16 | const ( 17 | BITS_PER_BYTE = 8 18 | MILLION_BIT = 1000000 19 | ) 20 | 21 | type NetIf struct { 22 | Iface string 23 | InBytes int64 24 | InPackages int64 25 | InErrors int64 26 | InDropped int64 27 | InFifoErrs int64 28 | InFrameErrs int64 29 | InCompressed int64 30 | InMulticast int64 31 | OutBytes int64 32 | OutPackages int64 33 | OutErrors int64 34 | OutDropped int64 35 | OutFifoErrs int64 36 | OutCollisions int64 37 | OutCarrierErrs int64 38 | OutCompressed int64 39 | TotalBytes int64 40 | TotalPackages int64 41 | TotalErrors int64 42 | TotalDropped int64 43 | SpeedBits int64 44 | InPercent float64 45 | OutPercent float64 46 | } 47 | 48 | func (this *NetIf) String() string { 49 | return fmt.Sprintf("", this.Iface, this.InBytes, this.InPackages) 50 | } 51 | 52 | /* 53 | Inter-| Receive | Transmit 54 | face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed 55 | eth0: 1990350 2838 0 0 0 0 0 0 401351 2218 0 0 0 0 0 0 56 | lo: 26105 286 0 0 0 0 0 0 26105 286 0 0 0 0 0 0 57 | */ 58 | func NetIfs(onlyPrefix []string) ([]*NetIf, error) { 59 | contents, err := ioutil.ReadFile(Root() + "/proc/net/dev") 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | ret := []*NetIf{} 65 | 66 | reader := bufio.NewReader(bytes.NewBuffer(contents)) 67 | for { 68 | lineBytes, err := file.ReadLine(reader) 69 | if err == io.EOF { 70 | err = nil 71 | break 72 | } else if err != nil { 73 | return nil, err 74 | } 75 | 76 | line := string(lineBytes) 77 | idx := strings.Index(line, ":") 78 | if idx < 0 { 79 | continue 80 | } 81 | 82 | netIf := NetIf{} 83 | 84 | eth := strings.TrimSpace(line[0:idx]) 85 | if len(onlyPrefix) > 0 { 86 | found := false 87 | for _, prefix := range onlyPrefix { 88 | if strings.HasPrefix(eth, prefix) { 89 | found = true 90 | break 91 | } 92 | } 93 | 94 | if !found { 95 | continue 96 | } 97 | } 98 | 99 | netIf.Iface = eth 100 | 101 | fields := strings.Fields(line[idx+1:]) 102 | 103 | if len(fields) != 16 { 104 | continue 105 | } 106 | 107 | netIf.InBytes, _ = strconv.ParseInt(fields[0], 10, 64) 108 | netIf.InPackages, _ = strconv.ParseInt(fields[1], 10, 64) 109 | netIf.InErrors, _ = strconv.ParseInt(fields[2], 10, 64) 110 | netIf.InDropped, _ = strconv.ParseInt(fields[3], 10, 64) 111 | netIf.InFifoErrs, _ = strconv.ParseInt(fields[4], 10, 64) 112 | netIf.InFrameErrs, _ = strconv.ParseInt(fields[5], 10, 64) 113 | netIf.InCompressed, _ = strconv.ParseInt(fields[6], 10, 64) 114 | netIf.InMulticast, _ = strconv.ParseInt(fields[7], 10, 64) 115 | 116 | netIf.OutBytes, _ = strconv.ParseInt(fields[8], 10, 64) 117 | netIf.OutPackages, _ = strconv.ParseInt(fields[9], 10, 64) 118 | netIf.OutErrors, _ = strconv.ParseInt(fields[10], 10, 64) 119 | netIf.OutDropped, _ = strconv.ParseInt(fields[11], 10, 64) 120 | netIf.OutFifoErrs, _ = strconv.ParseInt(fields[12], 10, 64) 121 | netIf.OutCollisions, _ = strconv.ParseInt(fields[13], 10, 64) 122 | netIf.OutCarrierErrs, _ = strconv.ParseInt(fields[14], 10, 64) 123 | netIf.OutCompressed, _ = strconv.ParseInt(fields[15], 10, 64) 124 | 125 | netIf.TotalBytes = netIf.InBytes + netIf.OutBytes 126 | netIf.TotalPackages = netIf.InPackages + netIf.OutPackages 127 | netIf.TotalErrors = netIf.InErrors + netIf.OutErrors 128 | netIf.TotalDropped = netIf.InDropped + netIf.OutDropped 129 | 130 | speedFile := fmt.Sprintf(Root()+"/sys/class/net/%s/speed", netIf.Iface) 131 | if content, err := ioutil.ReadFile(speedFile); err == nil { 132 | var speed int64 133 | speed, err = strconv.ParseInt(strings.TrimSpace(string(content)), 10, 64) 134 | if err != nil { 135 | netIf.SpeedBits = int64(0) 136 | netIf.InPercent = float64(0) 137 | netIf.OutPercent = float64(0) 138 | } else if speed == 0 { 139 | netIf.SpeedBits = int64(0) 140 | netIf.InPercent = float64(0) 141 | netIf.OutPercent = float64(0) 142 | } else { 143 | netIf.SpeedBits = speed * MILLION_BIT 144 | netIf.InPercent = float64(netIf.InBytes*BITS_PER_BYTE) * 100.0 / float64(netIf.SpeedBits) 145 | netIf.OutPercent = float64(netIf.OutBytes*BITS_PER_BYTE) * 100.0 / float64(netIf.SpeedBits) 146 | } 147 | } else { 148 | if content, err := sys.CmdOutBytes("ethtool", netIf.Iface); err == nil { 149 | var speed int64 150 | var speedStr string 151 | 152 | contentReader := bufio.NewReader(bytes.NewBuffer(content)) 153 | for { 154 | line, err := file.ReadLine(contentReader) 155 | 156 | if err == io.EOF { 157 | err = nil 158 | break 159 | } 160 | 161 | if err != nil { 162 | break 163 | } 164 | 165 | line = bytes.Trim(line, "\t") 166 | 167 | if bytes.HasPrefix(line, []byte("Speed:")) && bytes.HasSuffix(line, []byte("Mb/s")) { 168 | speedStr = string(line[7 : len(line)-4]) 169 | break 170 | } 171 | } 172 | 173 | speed, err = strconv.ParseInt(strings.TrimSpace(speedStr), 10, 64) 174 | if speedStr == "" || err != nil || speed == 0 { 175 | netIf.SpeedBits = int64(0) 176 | netIf.InPercent = float64(0) 177 | netIf.OutPercent = float64(0) 178 | } else { 179 | netIf.SpeedBits = speed * MILLION_BIT 180 | netIf.InPercent = float64(netIf.InBytes*BITS_PER_BYTE) * 100.0 / float64(netIf.SpeedBits) 181 | netIf.OutPercent = float64(netIf.OutBytes*BITS_PER_BYTE) * 100.0 / float64(netIf.SpeedBits) 182 | } 183 | } else { 184 | netIf.SpeedBits = int64(0) 185 | netIf.InPercent = float64(0) 186 | netIf.OutPercent = float64(0) 187 | } 188 | } 189 | 190 | ret = append(ret, &netIf) 191 | } 192 | 193 | return ret, nil 194 | } 195 | -------------------------------------------------------------------------------- /iostat.go: -------------------------------------------------------------------------------- 1 | package nux 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "strconv" 10 | "strings" 11 | "time" 12 | 13 | "github.com/toolkits/file" 14 | ) 15 | 16 | type DiskStats struct { 17 | Major int 18 | Minor int 19 | Device string 20 | ReadRequests uint64 // Total number of reads completed successfully. 21 | ReadMerged uint64 // Adjacent read requests merged in a single req. 22 | ReadSectors uint64 // Total number of sectors read successfully. 23 | MsecRead uint64 // Total number of ms spent by all reads. 24 | WriteRequests uint64 // total number of writes completed successfully. 25 | WriteMerged uint64 // Adjacent write requests merged in a single req. 26 | WriteSectors uint64 // total number of sectors written successfully. 27 | MsecWrite uint64 // Total number of ms spent by all writes. 28 | IosInProgress uint64 // Number of actual I/O requests currently in flight. 29 | MsecTotal uint64 // Amount of time during which ios_in_progress >= 1. 30 | MsecWeightedTotal uint64 // Measure of recent I/O completion time and backlog. 31 | DiscardRequests uint64 // total number of discards completed successfully. 32 | DiscardMerged uint64 // Adjacent discard requests merged in a single req. 33 | DiscardSectors uint64 // total number of sectors discarded successfully. 34 | MsecDiscard uint64 // Total number of ms spent by all discards. 35 | // Kernel 5.5+ appends two more fields for flush requests: 36 | FlushRequests uint64 37 | MsecFlush uint64 38 | 39 | TS time.Time 40 | } 41 | 42 | func (this *DiskStats) String() string { 43 | return fmt.Sprintf("", this.Device, this.Major, this.Minor, this.ReadRequests) 44 | } 45 | 46 | func ListDiskStats() ([]*DiskStats, error) { 47 | proc_diskstats := Root() + "/proc/diskstats" 48 | if !file.IsExist(proc_diskstats) { 49 | return nil, fmt.Errorf("%s not exists", proc_diskstats) 50 | } 51 | 52 | contents, err := ioutil.ReadFile(proc_diskstats) 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | ret := make([]*DiskStats, 0) 58 | 59 | reader := bufio.NewReader(bytes.NewBuffer(contents)) 60 | for { 61 | line, err := file.ReadLine(reader) 62 | if err == io.EOF { 63 | err = nil 64 | break 65 | } else if err != nil { 66 | return nil, err 67 | } 68 | 69 | fields := strings.Fields(string(line)) 70 | // shortcut the deduper and just skip disks that 71 | // haven't done a single read. This elimiates a bunch 72 | // of loopback, ramdisk, and cdrom devices but still 73 | // lets us report on the rare case that we actually use 74 | // a ramdisk. 75 | if fields[3] == "0" { 76 | continue 77 | } 78 | 79 | size := len(fields) 80 | // kernel version too low 81 | if size < 14 { 82 | continue 83 | } 84 | 85 | item := &DiskStats{} 86 | if item.Major, err = strconv.Atoi(fields[0]); err != nil { 87 | return nil, err 88 | } 89 | 90 | if item.Minor, err = strconv.Atoi(fields[1]); err != nil { 91 | return nil, err 92 | } 93 | 94 | item.Device = fields[2] 95 | 96 | if item.ReadRequests, err = strconv.ParseUint(fields[3], 10, 64); err != nil { 97 | return nil, err 98 | } 99 | 100 | if item.ReadMerged, err = strconv.ParseUint(fields[4], 10, 64); err != nil { 101 | return nil, err 102 | } 103 | 104 | if item.ReadSectors, err = strconv.ParseUint(fields[5], 10, 64); err != nil { 105 | return nil, err 106 | } 107 | 108 | if item.MsecRead, err = strconv.ParseUint(fields[6], 10, 64); err != nil { 109 | return nil, err 110 | } 111 | 112 | if item.WriteRequests, err = strconv.ParseUint(fields[7], 10, 64); err != nil { 113 | return nil, err 114 | } 115 | 116 | if item.WriteMerged, err = strconv.ParseUint(fields[8], 10, 64); err != nil { 117 | return nil, err 118 | } 119 | 120 | if item.WriteSectors, err = strconv.ParseUint(fields[9], 10, 64); err != nil { 121 | return nil, err 122 | } 123 | 124 | if item.MsecWrite, err = strconv.ParseUint(fields[10], 10, 64); err != nil { 125 | return nil, err 126 | } 127 | 128 | if item.IosInProgress, err = strconv.ParseUint(fields[11], 10, 64); err != nil { 129 | return nil, err 130 | } 131 | 132 | if item.MsecTotal, err = strconv.ParseUint(fields[12], 10, 64); err != nil { 133 | return nil, err 134 | } 135 | 136 | if item.MsecWeightedTotal, err = strconv.ParseUint(fields[13], 10, 64); err != nil { 137 | return nil, err 138 | } 139 | 140 | if size > 14 { 141 | if item.DiscardRequests, err = strconv.ParseUint(fields[14], 10, 64); err != nil { 142 | return nil, err 143 | } 144 | } 145 | 146 | if size > 15 { 147 | if item.DiscardMerged, err = strconv.ParseUint(fields[15], 10, 64); err != nil { 148 | return nil, err 149 | } 150 | } 151 | 152 | if size > 16 { 153 | if item.DiscardSectors, err = strconv.ParseUint(fields[16], 10, 64); err != nil { 154 | return nil, err 155 | } 156 | } 157 | 158 | if size > 17 { 159 | if item.MsecDiscard, err = strconv.ParseUint(fields[17], 10, 64); err != nil { 160 | return nil, err 161 | } 162 | } 163 | if size > 18 { 164 | if item.FlushRequests, err = strconv.ParseUint(fields[18], 10, 64); err != nil { 165 | return nil, err 166 | } 167 | } 168 | if size > 19 { 169 | if item.MsecFlush, err = strconv.ParseUint(fields[18], 10, 64); err != nil { 170 | return nil, err 171 | } 172 | } 173 | 174 | item.TS = time.Now() 175 | ret = append(ret, item) 176 | } 177 | return ret, nil 178 | } 179 | -------------------------------------------------------------------------------- /kernel.go: -------------------------------------------------------------------------------- 1 | package nux 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | "strings" 8 | 9 | "github.com/toolkits/file" 10 | ) 11 | 12 | func KernelMaxFiles() (uint64, error) { 13 | return file.ToUint64(Root() + "/proc/sys/fs/file-max") 14 | } 15 | 16 | func KernelAllocateFiles() (ret uint64, err error) { 17 | var content string 18 | file_nr := Root() + "/proc/sys/fs/file-nr" 19 | content, err = file.ToTrimString(file_nr) 20 | if err != nil { 21 | return 22 | } 23 | 24 | arr := strings.Fields(content) 25 | if len(arr) != 3 { 26 | err = fmt.Errorf("%s format error", file_nr) 27 | return 28 | } 29 | 30 | return strconv.ParseUint(arr[0], 10, 64) 31 | } 32 | 33 | func KernelMaxProc() (uint64, error) { 34 | return file.ToUint64(Root() + "/proc/sys/kernel/pid_max") 35 | } 36 | 37 | func KernelHostname() (string, error) { 38 | return os.Hostname() 39 | } 40 | -------------------------------------------------------------------------------- /loadavg.go: -------------------------------------------------------------------------------- 1 | package nux 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | 9 | "github.com/toolkits/file" 10 | ) 11 | 12 | type Loadavg struct { 13 | Avg1min float64 14 | Avg5min float64 15 | Avg15min float64 16 | RunningProcesses int64 17 | TotalProcesses int64 18 | } 19 | 20 | func (load *Loadavg) String() string { 21 | return fmt.Sprintf("<1min:%f, 5min:%f, 15min:%f, processes:%d/%d>", load.Avg1min, load.Avg5min, load.Avg15min, load.RunningProcesses, load.TotalProcesses) 22 | } 23 | 24 | func LoadAvg() (*Loadavg, error) { 25 | 26 | loadAvg := Loadavg{} 27 | 28 | data, err := file.ToTrimString(Root() + "/proc/loadavg") 29 | if err != nil { 30 | return nil, err 31 | } 32 | 33 | L := strings.Fields(data) 34 | if loadAvg.Avg1min, err = strconv.ParseFloat(L[0], 64); err != nil { 35 | return nil, err 36 | } 37 | if loadAvg.Avg5min, err = strconv.ParseFloat(L[1], 64); err != nil { 38 | return nil, err 39 | } 40 | if loadAvg.Avg15min, err = strconv.ParseFloat(L[2], 64); err != nil { 41 | return nil, err 42 | } 43 | processes := strings.SplitN(L[3], "/", 2) 44 | if len(processes) != 2 { 45 | return nil, errors.New("invalid loadavg " + data) 46 | } 47 | if loadAvg.RunningProcesses, err = strconv.ParseInt(processes[0], 10, 64); err != nil { 48 | return nil, err 49 | } 50 | if loadAvg.TotalProcesses, err = strconv.ParseInt(processes[1], 10, 64); err != nil { 51 | return nil, err 52 | } 53 | 54 | return &loadAvg, nil 55 | } 56 | -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | package nux 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestMetrics(t *testing.T) { 9 | 10 | fmt.Println("======kernel======") 11 | fmt.Print("KernelMaxFiles:") 12 | fmt.Println(KernelMaxFiles()) 13 | 14 | fmt.Print("KernelAllocateFiles:") 15 | fmt.Println(KernelAllocateFiles()) 16 | 17 | fmt.Print("KernelMaxProc:") 18 | fmt.Println(KernelMaxProc()) 19 | 20 | fmt.Print("KernelHostname:") 21 | fmt.Println(KernelHostname()) 22 | 23 | fmt.Println("======loadavg======") 24 | fmt.Print("LoadAvg:") 25 | fmt.Println(LoadAvg()) 26 | 27 | fmt.Println("======cpuinfo======") 28 | fmt.Print("NumCpu:") 29 | fmt.Println(NumCpu()) 30 | 31 | fmt.Print("CpuMHz:") 32 | fmt.Println(CpuMHz()) 33 | 34 | fmt.Println("======cpustat======") 35 | if ps, err := CurrentProcStat(); err != nil { 36 | fmt.Println("error:", err) 37 | } else { 38 | fmt.Print("CPU :") 39 | fmt.Println(ps.Cpu) 40 | for i, o := range ps.Cpus { 41 | fmt.Printf("CPU%d:", i) 42 | fmt.Println(o) 43 | } 44 | } 45 | 46 | fmt.Println("======dfstat======") 47 | if L, err := ListMountPoint(); err != nil { 48 | fmt.Println("error:", err) 49 | } else { 50 | for _, arr := range L { 51 | fmt.Println(BuildDeviceUsage(arr[0], arr[1], arr[2])) 52 | } 53 | } 54 | 55 | fmt.Println("======NetIfs======") 56 | if L, err := NetIfs([]string{}); err != nil { 57 | fmt.Println("error:", err) 58 | } else { 59 | for _, i := range L { 60 | fmt.Println(i) 61 | } 62 | } 63 | 64 | fmt.Println("======ListDiskStats======") 65 | if L, err := ListDiskStats(); err != nil { 66 | fmt.Println("error:", err) 67 | } else { 68 | for _, i := range L { 69 | fmt.Println(i) 70 | } 71 | } 72 | 73 | fmt.Println("======MemInfo:======") 74 | fmt.Println(MemInfo()) 75 | 76 | fmt.Println("=========TcpExt:=======") 77 | fmt.Println(Netstat("TcpExt")) 78 | 79 | fmt.Println("=========IpExt:=======") 80 | fmt.Println(Netstat("IpExt")) 81 | 82 | fmt.Println("=========ListeningPorts:=======") 83 | fmt.Println(ListeningPorts()) 84 | 85 | fmt.Println("=========Procs:=======") 86 | if L, err := AllProcs(); err != nil { 87 | fmt.Println("error:", err) 88 | } else { 89 | for i, item := range L { 90 | fmt.Println(item) 91 | if i == 10 { 92 | fmt.Println("and more...") 93 | break 94 | } 95 | } 96 | } 97 | 98 | fmt.Println("==============ss -s===============") 99 | fmt.Println(SocketStatSummary()) 100 | 101 | fmt.Println("=============uptime:==============") 102 | days, hours, mins, err := SystemUptime() 103 | if err != nil { 104 | fmt.Println("error:", err) 105 | } else { 106 | fmt.Printf("%d days, %d hours, %d mins\n", days, hours, mins) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /meminfo.go: -------------------------------------------------------------------------------- 1 | package nux 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "strconv" 10 | "strings" 11 | 12 | "github.com/toolkits/file" 13 | ) 14 | 15 | type Mem struct { 16 | Buffers uint64 17 | Cached uint64 18 | MemTotal uint64 19 | MemFree uint64 20 | MemAvailable uint64 21 | SwapTotal uint64 22 | SwapUsed uint64 23 | SwapFree uint64 24 | } 25 | 26 | func (this *Mem) String() string { 27 | return fmt.Sprintf("", this.MemTotal, this.MemFree, this.MemAvailable, this.Buffers, this.Cached) 28 | } 29 | 30 | var Multi uint64 = 1024 31 | 32 | var WANT = map[string]struct{}{ 33 | "Buffers:": struct{}{}, 34 | "Cached:": struct{}{}, 35 | "MemTotal:": struct{}{}, 36 | "MemFree:": struct{}{}, 37 | "MemAvailable:": struct{}{}, 38 | "SwapTotal:": struct{}{}, 39 | "SwapFree:": struct{}{}, 40 | } 41 | 42 | func MemInfo() (*Mem, error) { 43 | contents, err := ioutil.ReadFile(Root() + "/proc/meminfo") 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | memInfo := &Mem{} 49 | 50 | reader := bufio.NewReader(bytes.NewBuffer(contents)) 51 | 52 | for { 53 | line, err := file.ReadLine(reader) 54 | if err == io.EOF { 55 | err = nil 56 | break 57 | } else if err != nil { 58 | return nil, err 59 | } 60 | 61 | fields := strings.Fields(string(line)) 62 | fieldName := fields[0] 63 | 64 | _, ok := WANT[fieldName] 65 | if ok && len(fields) == 3 { 66 | val, numerr := strconv.ParseUint(fields[1], 10, 64) 67 | if numerr != nil { 68 | continue 69 | } 70 | switch fieldName { 71 | case "Buffers:": 72 | memInfo.Buffers = val * Multi 73 | case "Cached:": 74 | memInfo.Cached = val * Multi 75 | case "MemTotal:": 76 | memInfo.MemTotal = val * Multi 77 | case "MemFree:": 78 | memInfo.MemFree = val * Multi 79 | case "MemAvailable:": 80 | memInfo.MemAvailable = val * Multi 81 | case "SwapTotal:": 82 | memInfo.SwapTotal = val * Multi 83 | case "SwapFree:": 84 | memInfo.SwapFree = val * Multi 85 | } 86 | } 87 | } 88 | 89 | memInfo.SwapUsed = memInfo.SwapTotal - memInfo.SwapFree 90 | 91 | return memInfo, nil 92 | } 93 | -------------------------------------------------------------------------------- /netstat.go: -------------------------------------------------------------------------------- 1 | package nux 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "io" 7 | "io/ioutil" 8 | "strconv" 9 | "strings" 10 | 11 | "github.com/toolkits/file" 12 | ) 13 | 14 | // @param ext e.g. TcpExt or IpExt 15 | func Netstat(ext string) (ret map[string]uint64, err error) { 16 | ret = make(map[string]uint64) 17 | var contents []byte 18 | contents, err = ioutil.ReadFile(Root() + "/proc/net/netstat") 19 | if err != nil { 20 | return 21 | } 22 | 23 | reader := bufio.NewReader(bytes.NewBuffer(contents)) 24 | for { 25 | var bs []byte 26 | bs, err = file.ReadLine(reader) 27 | if err == io.EOF { 28 | err = nil 29 | break 30 | } else if err != nil { 31 | return 32 | } 33 | 34 | line := string(bs) 35 | idx := strings.Index(line, ":") 36 | if idx < 0 { 37 | continue 38 | } 39 | 40 | title := strings.TrimSpace(line[:idx]) 41 | if title == ext { 42 | ths := strings.Fields(strings.TrimSpace(line[idx+1:])) 43 | // the next line must be values 44 | bs, err = file.ReadLine(reader) 45 | if err != nil { 46 | return 47 | } 48 | 49 | valLine := string(bs) 50 | tds := strings.Fields(strings.TrimSpace(valLine[idx+1:])) 51 | for i := 0; i < len(ths); i++ { 52 | ret[ths[i]], err = strconv.ParseUint(tds[i], 10, 64) 53 | if err != nil { 54 | return 55 | } 56 | } 57 | 58 | return 59 | } 60 | 61 | } 62 | 63 | return 64 | } 65 | -------------------------------------------------------------------------------- /portstat.go: -------------------------------------------------------------------------------- 1 | package nux 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "io" 8 | "strconv" 9 | "strings" 10 | 11 | "github.com/toolkits/file" 12 | "github.com/toolkits/slice" 13 | "github.com/toolkits/sys" 14 | ) 15 | 16 | // ListeningPorts 为了兼容老代码 17 | func ListeningPorts() ([]int64, error) { 18 | return TcpPorts() 19 | } 20 | 21 | func TcpPorts() ([]int64, error) { 22 | return listeningPorts("sh", "-c", "ss -t -l -n") 23 | } 24 | 25 | func UdpPorts() ([]int64, error) { 26 | return listeningPorts("sh", "-c", "ss -u -a -n") 27 | } 28 | 29 | func listeningPorts(name string, args ...string) ([]int64, error) { 30 | ports := []int64{} 31 | 32 | bs, err := sys.CmdOutBytes(name, args...) 33 | if err != nil { 34 | return ports, err 35 | } 36 | 37 | reader := bufio.NewReader(bytes.NewBuffer(bs)) 38 | 39 | // ignore the first line 40 | line, err := file.ReadLine(reader) 41 | if err != nil { 42 | return ports, err 43 | } 44 | 45 | for { 46 | line, err = file.ReadLine(reader) 47 | if err == io.EOF { 48 | err = nil 49 | break 50 | } else if err != nil { 51 | return ports, err 52 | } 53 | 54 | fields := strings.Fields(string(line)) 55 | fieldsLen := len(fields) 56 | 57 | if fieldsLen != 4 && fieldsLen != 5 { 58 | return ports, fmt.Errorf("output of %s format not supported", name) 59 | } 60 | 61 | portColumnIndex := 2 62 | if fieldsLen == 5 { 63 | portColumnIndex = 3 64 | } 65 | 66 | location := strings.LastIndex(fields[portColumnIndex], ":") 67 | port := fields[portColumnIndex][location+1:] 68 | 69 | if p, e := strconv.ParseInt(port, 10, 64); e != nil { 70 | return ports, fmt.Errorf("parse port to int64 fail: %s", e.Error()) 71 | } else { 72 | ports = append(ports, p) 73 | } 74 | 75 | } 76 | 77 | return slice.UniqueInt64(ports), nil 78 | } 79 | -------------------------------------------------------------------------------- /proc.go: -------------------------------------------------------------------------------- 1 | package nux 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "strconv" 10 | "strings" 11 | 12 | "github.com/toolkits/file" 13 | ) 14 | 15 | type Proc struct { 16 | Pid int 17 | Name string 18 | Cmdline string 19 | } 20 | 21 | func (this *Proc) String() string { 22 | return fmt.Sprintf("", this.Pid, this.Name, this.Cmdline) 23 | } 24 | 25 | func AllProcs() (ps []*Proc, err error) { 26 | var dirs []string 27 | dirs, err = file.DirsUnder(Root() + "/proc") 28 | if err != nil { 29 | return 30 | } 31 | 32 | size := len(dirs) 33 | if size == 0 { 34 | return 35 | } 36 | 37 | for i := 0; i < size; i++ { 38 | pid, e := strconv.Atoi(dirs[i]) 39 | if e != nil { 40 | continue 41 | } 42 | 43 | statusFile := fmt.Sprintf(Root()+"/proc/%d/status", pid) 44 | cmdlineFile := fmt.Sprintf(Root()+"/proc/%d/cmdline", pid) 45 | if !file.IsExist(statusFile) || !file.IsExist(cmdlineFile) { 46 | continue 47 | } 48 | 49 | name, e := ReadName(statusFile) 50 | if e != nil { 51 | continue 52 | } 53 | 54 | cmdlineBytes, e := file.ToBytes(cmdlineFile) 55 | if e != nil { 56 | continue 57 | } 58 | 59 | cmdlineBytesLen := len(cmdlineBytes) 60 | if cmdlineBytesLen == 0 { 61 | continue 62 | } 63 | 64 | noNut := make([]byte, 0, cmdlineBytesLen) 65 | 66 | for j := 0; j < cmdlineBytesLen; j++ { 67 | if cmdlineBytes[j] != 0 { 68 | noNut = append(noNut, cmdlineBytes[j]) 69 | } 70 | } 71 | 72 | p := Proc{Pid: pid, Name: name, Cmdline: string(noNut)} 73 | ps = append(ps, &p) 74 | } 75 | 76 | return 77 | } 78 | 79 | func ReadName(path string) (name string, err error) { 80 | var content []byte 81 | content, err = ioutil.ReadFile(path) 82 | if err != nil { 83 | return 84 | } 85 | 86 | reader := bufio.NewReader(bytes.NewBuffer(content)) 87 | 88 | for { 89 | var bs []byte 90 | bs, err = file.ReadLine(reader) 91 | if err == io.EOF { 92 | return 93 | } 94 | 95 | line := string(bs) 96 | colonIndex := strings.Index(line, ":") 97 | 98 | if strings.TrimSpace(line[0:colonIndex]) == "Name" { 99 | return strings.TrimSpace(line[colonIndex+1:]), nil 100 | } 101 | 102 | } 103 | 104 | return 105 | } 106 | -------------------------------------------------------------------------------- /snmp.go: -------------------------------------------------------------------------------- 1 | package nux 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "io" 7 | "io/ioutil" 8 | "strconv" 9 | "strings" 10 | 11 | "github.com/toolkits/file" 12 | ) 13 | 14 | func Snmp(title string) (ret map[string]int64, err error) { 15 | ret = make(map[string]int64) 16 | var contents []byte 17 | contents, err = ioutil.ReadFile(Root() + "/proc/net/snmp") 18 | if err != nil { 19 | return 20 | } 21 | 22 | reader := bufio.NewReader(bytes.NewBuffer(contents)) 23 | for { 24 | var bs []byte 25 | bs, err = file.ReadLine(reader) 26 | if err == io.EOF { 27 | err = nil 28 | break 29 | } else if err != nil { 30 | return 31 | } 32 | 33 | line := string(bs) 34 | idx := strings.Index(line, ":") 35 | if idx < 0 { 36 | continue 37 | } 38 | 39 | prefix := strings.TrimSpace(line[:idx]) 40 | if prefix == title { 41 | ths := strings.Fields(strings.TrimSpace(line[idx+1:])) 42 | // the next line must be values 43 | bs, err = file.ReadLine(reader) 44 | if err != nil { 45 | return 46 | } 47 | 48 | valLine := string(bs) 49 | tds := strings.Fields(strings.TrimSpace(valLine[idx+1:])) 50 | for i := 0; i < len(ths); i++ { 51 | ret[ths[i]], err = strconv.ParseInt(tds[i], 10, 64) 52 | if err != nil { 53 | return 54 | } 55 | } 56 | 57 | return 58 | } 59 | 60 | } 61 | 62 | return 63 | } 64 | -------------------------------------------------------------------------------- /ss_s.go: -------------------------------------------------------------------------------- 1 | package nux 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "github.com/toolkits/file" 7 | "github.com/toolkits/sys" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | func SocketStatSummary() (m map[string]uint64, err error) { 13 | m = make(map[string]uint64) 14 | var bs []byte 15 | bs, err = sys.CmdOutBytes("sh", "-c", "ss -s") 16 | if err != nil { 17 | return 18 | } 19 | 20 | reader := bufio.NewReader(bytes.NewBuffer(bs)) 21 | 22 | // ignore the first line 23 | line, e := file.ReadLine(reader) 24 | if e != nil { 25 | return m, e 26 | } 27 | 28 | for { 29 | line, err = file.ReadLine(reader) 30 | if err != nil { 31 | return 32 | } 33 | 34 | lineStr := string(line) 35 | if strings.HasPrefix(lineStr, "TCP") { 36 | left := strings.Index(lineStr, "(") 37 | right := strings.Index(lineStr, ")") 38 | if left < 0 || right < 0 { 39 | continue 40 | } 41 | 42 | content := lineStr[left+1 : right] 43 | arr := strings.Split(content, ", ") 44 | for _, val := range arr { 45 | fields := strings.Fields(val) 46 | if fields[0] == "timewait" { 47 | timewait_arr := strings.Split(fields[1], "/") 48 | m["timewait"], _ = strconv.ParseUint(timewait_arr[0], 10, 64) 49 | if len(timewait_arr) > 1 { 50 | m["slabinfo.timewait"], _ = strconv.ParseUint(timewait_arr[1], 10, 64) 51 | } else { 52 | m["slabinfo.timewait"] = 0 53 | } 54 | continue 55 | } 56 | m[fields[0]], _ = strconv.ParseUint(fields[1], 10, 64) 57 | } 58 | return 59 | } 60 | } 61 | 62 | return 63 | } 64 | -------------------------------------------------------------------------------- /system.go: -------------------------------------------------------------------------------- 1 | package nux 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/toolkits/file" 9 | ) 10 | 11 | func SystemUptime() (days, hours, mins int64, err error) { 12 | var content string 13 | content, err = file.ToTrimString(Root() + "/proc/uptime") 14 | if err != nil { 15 | return 16 | } 17 | 18 | fields := strings.Fields(content) 19 | if len(fields) < 2 { 20 | err = fmt.Errorf(Root() + "/proc/uptime format not supported") 21 | return 22 | } 23 | 24 | secStr := fields[0] 25 | var secF float64 26 | secF, err = strconv.ParseFloat(secStr, 64) 27 | if err != nil { 28 | return 29 | } 30 | 31 | minTotal := secF / 60.0 32 | hourTotal := minTotal / 60.0 33 | 34 | days = int64(hourTotal / 24.0) 35 | hours = int64(hourTotal) - days*24 36 | mins = int64(minTotal) - (days * 60 * 24) - (hours * 60) 37 | 38 | return 39 | } 40 | --------------------------------------------------------------------------------