github.com/PandaGoAdmin/utils@v0.0.0-20211208134815-d5461603a00f/os_linux.go (about)

     1  // +build linux
     2  
     3  package kgo
     4  
     5  import (
     6  	"bufio"
     7  	"fmt"
     8  	"golang.org/x/sys/unix"
     9  	"io/ioutil"
    10  	"os"
    11  	"path/filepath"
    12  	"regexp"
    13  	"runtime"
    14  	"strconv"
    15  	"strings"
    16  	"syscall"
    17  )
    18  
    19  // getPidByInode 根据套接字的inode获取PID.须root权限.
    20  func getPidByInode(inode string, procDirs []string) (pid int) {
    21  	if len(procDirs) == 0 {
    22  		procDirs, _ = filepath.Glob("/proc/[0-9]*/fd/[0-9]*")
    23  	}
    24  
    25  	re := regexp.MustCompile(inode)
    26  	for _, item := range procDirs {
    27  		path, _ := os.Readlink(item)
    28  		out := re.FindString(path)
    29  		if len(out) != 0 {
    30  			pid, _ = strconv.Atoi(strings.Split(item, "/")[2])
    31  			break
    32  		}
    33  	}
    34  
    35  	return pid
    36  }
    37  
    38  // getProcessPathByPid 根据PID获取进程的执行路径.
    39  func getProcessPathByPid(pid int) (res string) {
    40  	exe := fmt.Sprintf("/proc/%d/exe", pid)
    41  	res, _ = os.Readlink(exe)
    42  
    43  	return
    44  }
    45  
    46  // MemoryUsage 获取内存使用率,单位字节.
    47  // 参数 virtual(仅支持linux),是否取虚拟内存.
    48  // used为已用,
    49  // free为空闲,
    50  // total为总数.
    51  func (ko *LkkOS) MemoryUsage(virtual bool) (used, free, total uint64) {
    52  	if virtual {
    53  		// 虚拟机的内存
    54  		contents, err := ioutil.ReadFile("/proc/meminfo")
    55  		if err == nil {
    56  			lines := strings.Split(string(contents), "\n")
    57  			for _, line := range lines {
    58  				fields := strings.Fields(line)
    59  				if len(fields) == 3 {
    60  					val, _ := strconv.ParseUint(fields[1], 10, 64) // kB
    61  
    62  					if strings.HasPrefix(fields[0], "MemTotal") {
    63  						total = val * 1024
    64  					} else if strings.HasPrefix(fields[0], "MemFree") {
    65  						free = val * 1024
    66  					}
    67  				}
    68  			}
    69  
    70  			//计算已用内存
    71  			used = total - free
    72  		}
    73  	} else {
    74  		// 真实物理机内存
    75  		sysi := &syscall.Sysinfo_t{}
    76  		err := syscall.Sysinfo(sysi)
    77  		if err == nil {
    78  			total = sysi.Totalram * uint64(sysi.Unit)
    79  			free = sysi.Freeram * uint64(sysi.Unit)
    80  			used = total - free
    81  		}
    82  	}
    83  
    84  	return
    85  }
    86  
    87  // CpuUsage 获取CPU使用率(darwin系统必须使用cgo),单位jiffies(节拍数).
    88  // user为用户态(用户进程)的运行时间,
    89  // idle为空闲时间,
    90  // total为累计时间.
    91  func (ko *LkkOS) CpuUsage() (user, idle, total uint64) {
    92  	contents, _ := ioutil.ReadFile("/proc/stat")
    93  	if len(contents) > 0 {
    94  		lines := strings.Split(string(contents), "\n")
    95  		for _, line := range lines {
    96  			fields := strings.Fields(line)
    97  			if fields[0] == "cpu" {
    98  				//CPU指标:user,nice, system, idle, iowait, irq, softirq
    99  				// cpu  130216 19944 162525 1491240 3784 24749 17773 0 0 0
   100  
   101  				numFields := len(fields)
   102  				for i := 1; i < numFields; i++ {
   103  					val, _ := strconv.ParseUint(fields[i], 10, 64)
   104  					total += val // tally up all the numbers to get total ticks
   105  					if i == 1 {
   106  						user = val
   107  					} else if i == 4 { // idle is the 5th field in the cpu line
   108  						idle = val
   109  					}
   110  				}
   111  				break
   112  			}
   113  		}
   114  	}
   115  
   116  	return
   117  }
   118  
   119  // DiskUsage 获取磁盘(目录)使用情况,单位字节.参数path为路径.
   120  // used为已用,
   121  // free为空闲,
   122  // total为总数.
   123  func (ko *LkkOS) DiskUsage(path string) (used, free, total uint64) {
   124  	fs := &syscall.Statfs_t{}
   125  	err := syscall.Statfs(path, fs)
   126  	if err == nil {
   127  		total = fs.Blocks * uint64(fs.Bsize)
   128  		free = fs.Bfree * uint64(fs.Bsize)
   129  		used = total - free
   130  	}
   131  
   132  	return
   133  }
   134  
   135  // Uptime 获取系统运行时间,秒.
   136  func (ko *LkkOS) Uptime() (uint64, error) {
   137  	sysinfo := &unix.Sysinfo_t{}
   138  	if err := unix.Sysinfo(sysinfo); err != nil {
   139  		return 0, err
   140  	}
   141  	return uint64(sysinfo.Uptime), nil
   142  }
   143  
   144  // GetBiosInfo 获取BIOS信息.
   145  // 注意:Mac机器没有BIOS信息,它使用EFI.
   146  func (ko *LkkOS) GetBiosInfo() *BiosInfo {
   147  	return &BiosInfo{
   148  		Vendor:  strings.TrimSpace(string(KFile.ReadFirstLine("/sys/class/dmi/id/bios_vendor"))),
   149  		Version: strings.TrimSpace(string(KFile.ReadFirstLine("/sys/class/dmi/id/bios_version"))),
   150  		Date:    strings.TrimSpace(string(KFile.ReadFirstLine("/sys/class/dmi/id/bios_date"))),
   151  	}
   152  }
   153  
   154  // GetBoardInfo 获取Board信息.
   155  func (ko *LkkOS) GetBoardInfo() *BoardInfo {
   156  	return &BoardInfo{
   157  		Name:     strings.TrimSpace(string(KFile.ReadFirstLine("/sys/class/dmi/id/board_name"))),
   158  		Vendor:   strings.TrimSpace(string(KFile.ReadFirstLine("/sys/class/dmi/id/board_vendor"))),
   159  		Version:  strings.TrimSpace(string(KFile.ReadFirstLine("/sys/class/dmi/id/board_version"))),
   160  		Serial:   strings.TrimSpace(string(KFile.ReadFirstLine("/sys/class/dmi/id/board_serial"))),
   161  		AssetTag: strings.TrimSpace(string(KFile.ReadFirstLine("/sys/class/dmi/id/board_asset_tag"))),
   162  	}
   163  }
   164  
   165  // GetCpuInfo 获取CPU信息.
   166  func (ko *LkkOS) GetCpuInfo() *CpuInfo {
   167  	var res = &CpuInfo{
   168  		Vendor:  "",
   169  		Model:   "",
   170  		Speed:   "",
   171  		Cache:   0,
   172  		Cpus:    0,
   173  		Cores:   0,
   174  		Threads: 0,
   175  	}
   176  
   177  	res.Threads = uint(runtime.NumCPU())
   178  	f, err := os.Open("/proc/cpuinfo")
   179  	if err == nil {
   180  		cpu := make(map[string]bool)
   181  		core := make(map[string]bool)
   182  		var cpuID string
   183  
   184  		s := bufio.NewScanner(f)
   185  		for s.Scan() {
   186  			if sl := cpuRegTwoColumns.Split(s.Text(), 2); sl != nil {
   187  				switch sl[0] {
   188  				case "physical id":
   189  					cpuID = sl[1]
   190  					cpu[cpuID] = true
   191  				case "core id":
   192  					coreID := fmt.Sprintf("%s/%s", cpuID, sl[1])
   193  					core[coreID] = true
   194  				case "vendor_id":
   195  					if res.Vendor == "" {
   196  						res.Vendor = sl[1]
   197  					}
   198  				case "model name":
   199  					if res.Model == "" {
   200  						// CPU model, as reported by /proc/cpuinfo, can be a bit ugly. Clean up...
   201  						model := cpuRegExtraSpace.ReplaceAllLiteralString(sl[1], " ")
   202  						res.Model = strings.Replace(model, "- ", "-", 1)
   203  					}
   204  				case "cpu MHz":
   205  					if res.Speed == "" {
   206  						res.Speed = sl[1]
   207  					}
   208  				case "cache size":
   209  					if res.Cache == 0 {
   210  						if m := cpuRegCacheSize.FindStringSubmatch(sl[1]); m != nil {
   211  							if cache, err := strconv.ParseUint(m[1], 10, 64); err == nil {
   212  								res.Cache = uint(cache)
   213  							}
   214  						}
   215  					}
   216  				}
   217  			}
   218  		}
   219  
   220  		res.Cpus = uint(len(cpu))
   221  		res.Cores = uint(len(core))
   222  	}
   223  	defer func() {
   224  		_ = f.Close()
   225  	}()
   226  
   227  	return res
   228  }
   229  
   230  // GetPidByPort 根据端口号获取监听的进程PID.
   231  // linux可能要求root权限;
   232  // darwin依赖lsof;
   233  // windows依赖netstat.
   234  func (ko *LkkOS) GetPidByPort(port int) (pid int) {
   235  	files := []string{
   236  		"/proc/net/tcp",
   237  		"/proc/net/udp",
   238  		"/proc/net/tcp6",
   239  		"/proc/net/udp6",
   240  	}
   241  
   242  	procDirs, _ := filepath.Glob("/proc/[0-9]*/fd/[0-9]*")
   243  	for _, fpath := range files {
   244  		lines, _ := KFile.ReadInArray(fpath)
   245  		for _, line := range lines[1:] {
   246  			fields := strings.Fields(line)
   247  			if len(fields) < 10 {
   248  				continue
   249  			}
   250  
   251  			//非 LISTEN 监听状态
   252  			if fields[3] != "0A" {
   253  				continue
   254  			}
   255  
   256  			//本地ip和端口
   257  			ipport := strings.Split(fields[1], ":")
   258  			locPort, _ := KConv.Hex2Dec(ipport[1])
   259  
   260  			// 非该端口
   261  			if int(locPort) != port {
   262  				continue
   263  			}
   264  
   265  			pid = getPidByInode(fields[9], procDirs)
   266  			if pid > 0 {
   267  				return
   268  			}
   269  		}
   270  	}
   271  
   272  	return
   273  }