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

     1  package kgo
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"net"
    10  	"net/http"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"regexp"
    15  	"runtime"
    16  	"runtime/debug"
    17  	"strings"
    18  	"unicode"
    19  )
    20  
    21  // SystemInfo 系统信息
    22  type SystemInfo struct {
    23  	ServerName   string  `json:"server_name"`    //服务器名称
    24  	SystemOs     string  `json:"system_os"`      //操作系统名称
    25  	Runtime      uint64  `json:"run_time"`       //服务运行时间,纳秒
    26  	Uptime       uint64  `json:"up_time"`        //操作系统运行时间,秒
    27  	GoroutineNum int     `json:"goroutine_num"`  //goroutine数量
    28  	CpuNum       int     `json:"cpu_num"`        //cpu核数
    29  	CpuUser      float64 `json:"cpu_user"`       //cpu用户态比率
    30  	CpuFree      float64 `json:"cpu_free"`       //cpu空闲比率
    31  	DiskUsed     uint64  `json:"disk_used"`      //已用磁盘空间,字节数
    32  	DiskFree     uint64  `json:"disk_free"`      //可用磁盘空间,字节数
    33  	DiskTotal    uint64  `json:"disk_total"`     //总磁盘空间,字节数
    34  	MemUsed      uint64  `json:"mem_used"`       //已用内存,字节数
    35  	MemSys       uint64  `json:"mem_sys"`        //系统内存占用量,字节数
    36  	MemFree      uint64  `json:"mem_free"`       //剩余内存,字节数
    37  	MemTotal     uint64  `json:"mem_total"`      //总内存,字节数
    38  	AllocGolang  uint64  `json:"alloc_golang"`   //golang内存使用量,字节数
    39  	AllocTotal   uint64  `json:"alloc_total"`    //总分配的内存,字节数
    40  	Lookups      uint64  `json:"lookups"`        //指针查找次数
    41  	Mallocs      uint64  `json:"mallocs"`        //内存分配次数
    42  	Frees        uint64  `json:"frees"`          //内存释放次数
    43  	LastGCTime   uint64  `json:"last_gc_time"`   //上次GC时间,纳秒
    44  	NextGC       uint64  `json:"next_gc"`        //下次GC内存回收量,字节数
    45  	PauseTotalNs uint64  `json:"pause_total_ns"` //GC暂停时间总量,纳秒
    46  	PauseNs      uint64  `json:"pause_ns"`       //上次GC暂停时间,纳秒
    47  }
    48  
    49  // BiosInfo BIOS信息
    50  type BiosInfo struct {
    51  	Vendor  string `json:"vendor"`
    52  	Version string `json:"version"`
    53  	Date    string `json:"date"`
    54  }
    55  
    56  // BoardInfo Board信息
    57  type BoardInfo struct {
    58  	Name     string `json:"name"`
    59  	Vendor   string `json:"vendor"`
    60  	Version  string `json:"version"`
    61  	Serial   string `json:"serial"`
    62  	AssetTag string `json:"assettag"`
    63  }
    64  
    65  // CpuInfo CPU信息
    66  type CpuInfo struct {
    67  	Vendor  string `json:"vendor"`
    68  	Model   string `json:"model"`
    69  	Speed   string `json:"speed"`   // CPU clock rate in MHz
    70  	Cache   uint   `json:"cache"`   // CPU cache size in KB
    71  	Cpus    uint   `json:"cpus"`    // number of physical CPUs
    72  	Cores   uint   `json:"cores"`   // number of physical CPU cores
    73  	Threads uint   `json:"threads"` // number of logical (HT) CPU cores
    74  }
    75  
    76  var (
    77  	cpuRegTwoColumns = regexp.MustCompile("\t+: ")
    78  	cpuRegExtraSpace = regexp.MustCompile(" +")
    79  	cpuRegCacheSize  = regexp.MustCompile(`^(\d+) KB$`)
    80  )
    81  
    82  // IsWindows 当前操作系统是否Windows.
    83  func (ko *LkkOS) IsWindows() bool {
    84  	return "windows" == runtime.GOOS
    85  }
    86  
    87  // IsLinux 当前操作系统是否Linux.
    88  func (ko *LkkOS) IsLinux() bool {
    89  	return "linux" == runtime.GOOS
    90  }
    91  
    92  // IsMac 当前操作系统是否Mac OS/X.
    93  func (ko *LkkOS) IsMac() bool {
    94  	return "darwin" == runtime.GOOS
    95  }
    96  
    97  // Pwd 获取当前程序运行所在的路径,注意和Getwd有所不同.
    98  // 若当前执行的是链接文件,则会指向真实二进制程序的所在目录.
    99  func (ko *LkkOS) Pwd() string {
   100  	var dir, ex string
   101  	var err error
   102  	ex, err = os.Executable()
   103  	if err == nil {
   104  		exReal, _ := filepath.EvalSymlinks(ex)
   105  		exReal, _ = filepath.Abs(exReal)
   106  		dir = filepath.Dir(exReal)
   107  	}
   108  
   109  	return dir
   110  }
   111  
   112  // Getcwd 取得当前工作目录(程序可能在任务中进行多次目录切换).
   113  func (ko *LkkOS) Getcwd() (string, error) {
   114  	dir, err := os.Getwd()
   115  	return dir, err
   116  }
   117  
   118  // Chdir 改变/进入新的工作目录.
   119  func (ko *LkkOS) Chdir(dir string) error {
   120  	return os.Chdir(dir)
   121  }
   122  
   123  // LocalIP 获取本机第一个NIC's IP.
   124  func (ko *LkkOS) LocalIP() (string, error) {
   125  	res := ""
   126  	addrs, err := net.InterfaceAddrs()
   127  	if len(addrs) > 0 {
   128  		for _, addr := range addrs {
   129  			if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
   130  				if nil != ipnet.IP.To4() {
   131  					res = ipnet.IP.String()
   132  					break
   133  				}
   134  			}
   135  		}
   136  	}
   137  
   138  	return res, err
   139  }
   140  
   141  // OutboundIP 获取本机的出口IP.
   142  func (ko *LkkOS) OutboundIP() (string, error) {
   143  	res := ""
   144  	conn, err := net.Dial("udp", "8.8.8.8:80")
   145  	if conn != nil {
   146  		addr := conn.LocalAddr().(*net.UDPAddr)
   147  		res = addr.IP.String()
   148  		_ = conn.Close()
   149  	}
   150  
   151  	return res, err
   152  }
   153  
   154  // PrivateCIDR 获取私有网段的CIDR(无类别域间路由).
   155  func (ko *LkkOS) PrivateCIDR() []*net.IPNet {
   156  	maxCidrBlocks := []string{
   157  		"127.0.0.1/8",    // localhost
   158  		"10.0.0.0/8",     // 24-bit block
   159  		"172.16.0.0/12",  // 20-bit block
   160  		"192.168.0.0/16", // 16-bit block
   161  		"169.254.0.0/16", // link local address
   162  		"::1/128",        // localhost IPv6
   163  		"fc00::/7",       // unique local address IPv6
   164  		"fe80::/10",      // link local address IPv6
   165  	}
   166  
   167  	res := make([]*net.IPNet, len(maxCidrBlocks))
   168  	for i, maxCidrBlock := range maxCidrBlocks {
   169  		_, cidr, _ := net.ParseCIDR(maxCidrBlock)
   170  		res[i] = cidr
   171  	}
   172  
   173  	return res
   174  }
   175  
   176  // IsPrivateIp 是否私有IP地址(ipv4/ipv6).
   177  func (ko *LkkOS) IsPrivateIp(str string) (bool, error) {
   178  	ip := net.ParseIP(str)
   179  	if ip == nil {
   180  		return false, errors.New("[IsPrivateIp]`str is not valid ip")
   181  	}
   182  
   183  	if KPrivCidrs == nil {
   184  		KPrivCidrs = ko.PrivateCIDR()
   185  	}
   186  	for i := range KPrivCidrs {
   187  		if KPrivCidrs[i].Contains(ip) {
   188  			return true, nil
   189  		}
   190  	}
   191  
   192  	return false, nil
   193  }
   194  
   195  // IsPublicIP 是否公网IPv4.
   196  func (ko *LkkOS) IsPublicIP(str string) (bool, error) {
   197  	ip := net.ParseIP(str)
   198  	if ip == nil {
   199  		return false, errors.New("[IsPublicIP]`str is not valid ip")
   200  	}
   201  
   202  	if ip.IsLoopback() || ip.IsLinkLocalMulticast() || ip.IsLinkLocalUnicast() {
   203  		return false, nil
   204  	}
   205  	if ip4 := ip.To4(); ip4 != nil {
   206  		switch true {
   207  		case ip4[0] == 10:
   208  			return false, nil
   209  		case ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31:
   210  			return false, nil
   211  		case ip4[0] == 192 && ip4[1] == 168:
   212  			return false, nil
   213  		}
   214  	}
   215  
   216  	return true, nil
   217  }
   218  
   219  // GetIPs 获取本机的IP列表.
   220  func (ko *LkkOS) GetIPs() (ips []string) {
   221  	interfaceAddrs, _ := net.InterfaceAddrs()
   222  	if len(interfaceAddrs) > 0 {
   223  		for _, addr := range interfaceAddrs {
   224  			ipNet, isValidIpNet := addr.(*net.IPNet)
   225  			if isValidIpNet && !ipNet.IP.IsLoopback() {
   226  				if ipNet.IP.To4() != nil {
   227  					ips = append(ips, ipNet.IP.String())
   228  				}
   229  			}
   230  		}
   231  	}
   232  
   233  	return
   234  }
   235  
   236  // GetMacAddrs 获取本机的Mac网卡地址列表.
   237  func (ko *LkkOS) GetMacAddrs() (macAddrs []string) {
   238  	netInterfaces, _ := net.Interfaces()
   239  	if len(netInterfaces) > 0 {
   240  		for _, netInterface := range netInterfaces {
   241  			macAddr := netInterface.HardwareAddr.String()
   242  			if len(macAddr) == 0 {
   243  				continue
   244  			}
   245  			macAddrs = append(macAddrs, macAddr)
   246  		}
   247  	}
   248  
   249  	return
   250  }
   251  
   252  // Hostname 获取主机名.
   253  func (ko *LkkOS) Hostname() (string, error) {
   254  	return os.Hostname()
   255  }
   256  
   257  // GetIpByHostname 返回主机名对应的 IPv4地址.
   258  func (ko *LkkOS) GetIpByHostname(hostname string) (string, error) {
   259  	ips, err := net.LookupIP(hostname)
   260  	if ips != nil {
   261  		for _, v := range ips {
   262  			if v.To4() != nil {
   263  				return v.String(), nil
   264  			}
   265  		}
   266  		return "", nil
   267  	}
   268  	return "", err
   269  }
   270  
   271  // GetIpsByHost 获取互联网域名/主机名对应的 IPv4 地址列表.
   272  func (ko *LkkOS) GetIpsByDomain(domain string) ([]string, error) {
   273  	ips, err := net.LookupIP(domain)
   274  	if ips != nil {
   275  		var ipstrs []string
   276  		for _, v := range ips {
   277  			if v.To4() != nil {
   278  				ipstrs = append(ipstrs, v.String())
   279  			}
   280  		}
   281  		return ipstrs, nil
   282  	}
   283  	return nil, err
   284  }
   285  
   286  // GetHostByIp 获取指定的IP地址对应的主机名.
   287  func (ko *LkkOS) GetHostByIp(ipAddress string) (string, error) {
   288  	names, err := net.LookupAddr(ipAddress)
   289  	if names != nil {
   290  		return strings.TrimRight(names[0], "."), nil
   291  	}
   292  	return "", err
   293  }
   294  
   295  // Setenv 设置一个环境变量的值.
   296  func (ko *LkkOS) Setenv(varname, data string) error {
   297  	return os.Setenv(varname, data)
   298  }
   299  
   300  // Getenv 获取一个环境变量的值.defvalue为默认值.
   301  func (ko *LkkOS) Getenv(varname string, defvalue ...string) string {
   302  	val := os.Getenv(varname)
   303  	if val == "" && len(defvalue) > 0 {
   304  		val = defvalue[0]
   305  	}
   306  
   307  	return val
   308  }
   309  
   310  // Unsetenv 删除一个环境变量.
   311  func (ko *LkkOS) Unsetenv(varname string) error {
   312  	return os.Unsetenv(varname)
   313  }
   314  
   315  // GetEndian 获取系统字节序类型,小端返回binary.LittleEndian,大端返回binary.BigEndian .
   316  func (ko *LkkOS) GetEndian() binary.ByteOrder {
   317  	return getEndian()
   318  }
   319  
   320  // IsLittleEndian 系统字节序类型是否小端存储.
   321  func (ko *LkkOS) IsLittleEndian() bool {
   322  	return isLittleEndian()
   323  }
   324  
   325  // Exec 执行一个外部命令.
   326  // retInt为1时失败,为0时成功;outStr为执行命令的输出;errStr为错误输出.
   327  // 命令如
   328  // "ls -a"
   329  // "/bin/bash -c \"ls -a\""
   330  func (ko *LkkOS) Exec(command string) (retInt int, outStr, errStr []byte) {
   331  	// split command
   332  	q := rune(0)
   333  	parts := strings.FieldsFunc(command, func(r rune) bool {
   334  		switch {
   335  		case r == q:
   336  			q = rune(0)
   337  			return false
   338  		case q != rune(0):
   339  			return false
   340  		case unicode.In(r, unicode.Quotation_Mark):
   341  			q = r
   342  			return false
   343  		default:
   344  			return unicode.IsSpace(r)
   345  		}
   346  	})
   347  
   348  	// remove the " and ' on both sides
   349  	for i, v := range parts {
   350  		f, l := v[0], len(v)
   351  		if l >= 2 && (f == '"' || f == '\'') {
   352  			parts[i] = v[1 : l-1]
   353  		}
   354  	}
   355  
   356  	var stdout, stderr bytes.Buffer
   357  	cmd := exec.Command(parts[0], parts[1:]...)
   358  	cmd.Stdout = &stdout
   359  	cmd.Stderr = &stderr
   360  	err := cmd.Run()
   361  	if err != nil {
   362  		retInt = 1 //失败
   363  		stderr.WriteString(err.Error())
   364  		errStr = stderr.Bytes()
   365  	} else {
   366  		retInt = 0 //成功
   367  		outStr, errStr = stdout.Bytes(), stderr.Bytes()
   368  	}
   369  
   370  	return
   371  }
   372  
   373  // System 与Exec相同,但会同时打印标准输出和标准错误.
   374  func (ko *LkkOS) System(command string) (retInt int, outStr, errStr []byte) {
   375  	// split command
   376  	q := rune(0)
   377  	parts := strings.FieldsFunc(command, func(r rune) bool {
   378  		switch {
   379  		case r == q:
   380  			q = rune(0)
   381  			return false
   382  		case q != rune(0):
   383  			return false
   384  		case unicode.In(r, unicode.Quotation_Mark):
   385  			q = r
   386  			return false
   387  		default:
   388  			return unicode.IsSpace(r)
   389  		}
   390  	})
   391  
   392  	// remove the " and ' on both sides
   393  	for i, v := range parts {
   394  		f, l := v[0], len(v)
   395  		if l >= 2 && (f == '"' || f == '\'') {
   396  			parts[i] = v[1 : l-1]
   397  		}
   398  	}
   399  
   400  	var stdout, stderr bytes.Buffer
   401  	var err error
   402  
   403  	cmd := exec.Command(parts[0], parts[1:]...)
   404  	stdoutIn, _ := cmd.StdoutPipe()
   405  	stderrIn, _ := cmd.StderrPipe()
   406  	outWr := io.MultiWriter(os.Stdout, &stdout)
   407  	errWr := io.MultiWriter(os.Stderr, &stderr)
   408  
   409  	err = cmd.Start()
   410  	if err != nil {
   411  		retInt = 1 //失败
   412  		stderr.WriteString(err.Error())
   413  		fmt.Printf("%s\n", stderr.Bytes())
   414  		return
   415  	}
   416  
   417  	_, _ = io.Copy(outWr, stdoutIn)
   418  	_, _ = io.Copy(errWr, stderrIn)
   419  	err = cmd.Wait()
   420  	if err != nil {
   421  		stderr.WriteString(err.Error())
   422  		fmt.Println(stderr.Bytes())
   423  		retInt = 1 //失败
   424  	} else {
   425  		retInt = 0 //成功
   426  	}
   427  	outStr, errStr = stdout.Bytes(), stderr.Bytes()
   428  
   429  	return
   430  }
   431  
   432  // Chmod 改变文件模式.
   433  func (ko *LkkOS) Chmod(filename string, mode os.FileMode) bool {
   434  	return os.Chmod(filename, mode) == nil
   435  }
   436  
   437  // Chown 改变文件的所有者.
   438  func (ko *LkkOS) Chown(filename string, uid, gid int) bool {
   439  	return os.Chown(filename, uid, gid) == nil
   440  }
   441  
   442  // GetTempDir 返回用于临时文件的目录.
   443  func (ko *LkkOS) GetTempDir() string {
   444  	return os.TempDir()
   445  }
   446  
   447  // ClientIp 获取客户端真实IP,req为http请求.
   448  func (ko *LkkOS) ClientIp(req *http.Request) string {
   449  	// 获取头部信息,有可能是代理
   450  	xRealIP := req.Header.Get("X-Real-Ip")
   451  	xForwardedFor := req.Header.Get("X-Forwarded-For")
   452  
   453  	// If both empty, return IP from remote address
   454  	if xRealIP == "" && xForwardedFor == "" {
   455  		var remoteIP string
   456  
   457  		// If there are colon in remote address, remove the port number
   458  		// otherwise, return remote address as is
   459  		if strings.ContainsRune(req.RemoteAddr, ':') {
   460  			remoteIP, _, _ = net.SplitHostPort(req.RemoteAddr)
   461  		} else {
   462  			remoteIP = req.RemoteAddr
   463  		}
   464  
   465  		return remoteIP
   466  	}
   467  
   468  	// Check list of IP in X-Forwarded-For and return the first global address
   469  	// X-Forwarded-For是逗号分隔的IP地址列表,如"10.0.0.1, 10.0.0.2, 10.0.0.3"
   470  	for _, address := range strings.Split(xForwardedFor, ",") {
   471  		address = strings.TrimSpace(address)
   472  		isPrivate, err := ko.IsPrivateIp(address)
   473  		if !isPrivate && err == nil {
   474  			return address
   475  		}
   476  	}
   477  
   478  	if xRealIP == "::1" {
   479  		xRealIP = "127.0.0.1"
   480  	}
   481  
   482  	// If nothing succeed, return X-Real-IP
   483  	return xRealIP
   484  }
   485  
   486  // IsPortOpen 检查主机端口是否开放.
   487  // host为主机名;port为(整型/字符串)端口号;protocols为协议名称,可选,默认tcp.
   488  func (ko *LkkOS) IsPortOpen(host string, port interface{}, protocols ...string) bool {
   489  	if KStr.IsHost(host) && isPort(port) {
   490  		// 默认tcp协议
   491  		protocol := "tcp"
   492  		if len(protocols) > 0 && len(protocols[0]) > 0 {
   493  			protocol = strings.ToLower(protocols[0])
   494  		}
   495  
   496  		conn, _ := net.DialTimeout(protocol, net.JoinHostPort(host, KConv.ToStr(port)), CHECK_CONNECT_TIMEOUT)
   497  		if conn != nil {
   498  			_ = conn.Close()
   499  			return true
   500  		}
   501  	}
   502  
   503  	return false
   504  }
   505  
   506  // ForceGC 强制手动GC垃圾回收(阻塞).
   507  func (ko *LkkOS) ForceGC() {
   508  	runtime.GC()
   509  	debug.FreeOSMemory()
   510  }
   511  
   512  // TriggerGC 触发GC(非阻塞).
   513  func (ko *LkkOS) TriggerGC() {
   514  	go func() {
   515  		ko.ForceGC()
   516  	}()
   517  }
   518  
   519  // MemoryGetUsage 获取当前go程序的内存使用,返回字节数.
   520  func (ko *LkkOS) GoMemory() uint64 {
   521  	stat := new(runtime.MemStats)
   522  	runtime.ReadMemStats(stat)
   523  	return stat.Alloc
   524  }
   525  
   526  // GetSystemInfo 获取系统运行信息.
   527  func (ko *LkkOS) GetSystemInfo() *SystemInfo {
   528  	//运行时信息
   529  	mstat := &runtime.MemStats{}
   530  	runtime.ReadMemStats(mstat)
   531  
   532  	//CPU信息
   533  	cpuUser, cpuIdel, cpuTotal := ko.CpuUsage()
   534  	cpuUserRate := float64(cpuUser) / float64(cpuTotal)
   535  	cpuFreeRate := float64(cpuIdel) / float64(cpuTotal)
   536  
   537  	//磁盘空间信息
   538  	var diskUsed, diskFree, diskTotal uint64
   539  	if runtime.GOOS == "windows" {
   540  		//TODO 待修改
   541  		diskUsed, diskFree, diskTotal = ko.DiskUsage("C:")
   542  	} else {
   543  		diskUsed, diskFree, diskTotal = ko.DiskUsage("/")
   544  	}
   545  
   546  	//内存使用信息
   547  	memUsed, memFree, memTotal := ko.MemoryUsage(true)
   548  
   549  	serverName, _ := os.Hostname()
   550  	uptime, _ := ko.Uptime()
   551  
   552  	return &SystemInfo{
   553  		ServerName:   serverName,
   554  		SystemOs:     runtime.GOOS,
   555  		Runtime:      uint64(KTime.ServiceUptime()),
   556  		Uptime:       uptime,
   557  		GoroutineNum: runtime.NumGoroutine(),
   558  		CpuNum:       runtime.NumCPU(),
   559  		CpuUser:      cpuUserRate,
   560  		CpuFree:      cpuFreeRate,
   561  		DiskUsed:     diskUsed,
   562  		DiskFree:     diskFree,
   563  		DiskTotal:    diskTotal,
   564  		MemUsed:      memUsed,
   565  		MemSys:       mstat.Sys,
   566  		MemFree:      memFree,
   567  		MemTotal:     memTotal,
   568  		AllocGolang:  mstat.Alloc,
   569  		AllocTotal:   mstat.TotalAlloc,
   570  		Lookups:      mstat.Lookups,
   571  		Mallocs:      mstat.Mallocs,
   572  		Frees:        mstat.Frees,
   573  		LastGCTime:   mstat.LastGC,
   574  		NextGC:       mstat.NextGC,
   575  		PauseTotalNs: mstat.PauseTotalNs,
   576  		PauseNs:      mstat.PauseNs[(mstat.NumGC+255)%256],
   577  	}
   578  }
   579  
   580  // GetProcessExecPath 根据PID获取进程的执行路径.
   581  func (ko *LkkOS) GetProcessExecPath(pid int) string {
   582  	return getProcessPathByPid(pid)
   583  }