host_solaris.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. package host
  2. import (
  3. "bufio"
  4. "bytes"
  5. "context"
  6. "fmt"
  7. "io/ioutil"
  8. "os"
  9. "os/exec"
  10. "regexp"
  11. "strconv"
  12. "strings"
  13. "github.com/shirou/gopsutil/internal/common"
  14. )
  15. func HostIDWithContext(ctx context.Context) (string, error) {
  16. platform, err := parseReleaseFile()
  17. if err != nil {
  18. return "", err
  19. }
  20. if platform == "SmartOS" {
  21. // If everything works, use the current zone ID as the HostID if present.
  22. zonename, err := exec.LookPath("zonename")
  23. if err == nil {
  24. out, err := invoke.CommandWithContext(ctx, zonename)
  25. if err == nil {
  26. sc := bufio.NewScanner(bytes.NewReader(out))
  27. for sc.Scan() {
  28. line := sc.Text()
  29. // If we're in the global zone, rely on the hostname.
  30. if line == "global" {
  31. hostname, err := os.Hostname()
  32. if err == nil {
  33. return hostname, nil
  34. }
  35. } else {
  36. return strings.TrimSpace(line), nil
  37. }
  38. }
  39. }
  40. }
  41. }
  42. // If HostID is still unknown, use hostid(1), which can lie to callers but at
  43. // this point there are no hardware facilities available. This behavior
  44. // matches that of other supported OSes.
  45. hostID, err := exec.LookPath("hostid")
  46. if err == nil {
  47. out, err := invoke.CommandWithContext(ctx, hostID)
  48. if err == nil {
  49. sc := bufio.NewScanner(bytes.NewReader(out))
  50. for sc.Scan() {
  51. line := sc.Text()
  52. return strings.TrimSpace(line), nil
  53. }
  54. }
  55. }
  56. return "", nil
  57. }
  58. // Count number of processes based on the number of entries in /proc
  59. func numProcs(ctx context.Context) (uint64, error) {
  60. dirs, err := ioutil.ReadDir("/proc")
  61. if err != nil {
  62. return 0, err
  63. }
  64. return uint64(len(dirs)), nil
  65. }
  66. var kstatMatch = regexp.MustCompile(`([^\s]+)[\s]+([^\s]*)`)
  67. func BootTimeWithContext(ctx context.Context) (uint64, error) {
  68. kstat, err := exec.LookPath("kstat")
  69. if err != nil {
  70. return 0, err
  71. }
  72. out, err := invoke.CommandWithContext(ctx, kstat, "-p", "unix:0:system_misc:boot_time")
  73. if err != nil {
  74. return 0, err
  75. }
  76. kstats := kstatMatch.FindAllStringSubmatch(string(out), -1)
  77. if len(kstats) != 1 {
  78. return 0, fmt.Errorf("expected 1 kstat, found %d", len(kstats))
  79. }
  80. return strconv.ParseUint(kstats[0][2], 10, 64)
  81. }
  82. func UptimeWithContext(ctx context.Context) (uint64, error) {
  83. bootTime, err := BootTime()
  84. if err != nil {
  85. return 0, err
  86. }
  87. return timeSince(bootTime), nil
  88. }
  89. func UsersWithContext(ctx context.Context) ([]UserStat, error) {
  90. return []UserStat{}, common.ErrNotImplementedError
  91. }
  92. func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
  93. return []TemperatureStat{}, common.ErrNotImplementedError
  94. }
  95. func VirtualizationWithContext(ctx context.Context) (string, string, error) {
  96. return "", "", common.ErrNotImplementedError
  97. }
  98. // Find distribution name from /etc/release
  99. func parseReleaseFile() (string, error) {
  100. b, err := ioutil.ReadFile("/etc/release")
  101. if err != nil {
  102. return "", err
  103. }
  104. s := string(b)
  105. s = strings.TrimSpace(s)
  106. var platform string
  107. switch {
  108. case strings.HasPrefix(s, "SmartOS"):
  109. platform = "SmartOS"
  110. case strings.HasPrefix(s, "OpenIndiana"):
  111. platform = "OpenIndiana"
  112. case strings.HasPrefix(s, "OmniOS"):
  113. platform = "OmniOS"
  114. case strings.HasPrefix(s, "Open Storage"):
  115. platform = "NexentaStor"
  116. case strings.HasPrefix(s, "Solaris"):
  117. platform = "Solaris"
  118. case strings.HasPrefix(s, "Oracle Solaris"):
  119. platform = "Solaris"
  120. default:
  121. platform = strings.Fields(s)[0]
  122. }
  123. return platform, nil
  124. }
  125. // parseUnameOutput returns platformFamily, kernelVersion and platformVersion
  126. func parseUnameOutput(ctx context.Context) (string, string, string, error) {
  127. uname, err := exec.LookPath("uname")
  128. if err != nil {
  129. return "", "", "", err
  130. }
  131. out, err := invoke.CommandWithContext(ctx, uname, "-srv")
  132. if err != nil {
  133. return "", "", "", err
  134. }
  135. fields := strings.Fields(string(out))
  136. if len(fields) < 3 {
  137. return "", "", "", fmt.Errorf("malformed `uname` output")
  138. }
  139. return fields[0], fields[1], fields[2], nil
  140. }
  141. func KernelVersionWithContext(ctx context.Context) (string, error) {
  142. _, kernelVersion, _, err := parseUnameOutput(ctx)
  143. return kernelVersion, err
  144. }
  145. func PlatformInformationWithContext(ctx context.Context) (string, string, string, error) {
  146. platform, err := parseReleaseFile()
  147. if err != nil {
  148. return "", "", "", err
  149. }
  150. platformFamily, _, platformVersion, err := parseUnameOutput(ctx)
  151. if err != nil {
  152. return "", "", "", err
  153. }
  154. return platform, platformFamily, platformVersion, nil
  155. }