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 }