disk_freebsd.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. //go:build freebsd
  2. // +build freebsd
  3. package disk
  4. import (
  5. "bufio"
  6. "bytes"
  7. "context"
  8. "encoding/binary"
  9. "fmt"
  10. "strconv"
  11. "strings"
  12. "golang.org/x/sys/unix"
  13. "github.com/shirou/gopsutil/v3/internal/common"
  14. )
  15. // PartitionsWithContext returns disk partition.
  16. // 'all' argument is ignored, see: https://github.com/giampaolo/psutil/issues/906
  17. func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
  18. var ret []PartitionStat
  19. // get length
  20. count, err := unix.Getfsstat(nil, unix.MNT_WAIT)
  21. if err != nil {
  22. return ret, err
  23. }
  24. fs := make([]unix.Statfs_t, count)
  25. if _, err = unix.Getfsstat(fs, unix.MNT_WAIT); err != nil {
  26. return ret, err
  27. }
  28. for _, stat := range fs {
  29. opts := []string{"rw"}
  30. if stat.Flags&unix.MNT_RDONLY != 0 {
  31. opts = []string{"ro"}
  32. }
  33. if stat.Flags&unix.MNT_SYNCHRONOUS != 0 {
  34. opts = append(opts, "sync")
  35. }
  36. if stat.Flags&unix.MNT_NOEXEC != 0 {
  37. opts = append(opts, "noexec")
  38. }
  39. if stat.Flags&unix.MNT_NOSUID != 0 {
  40. opts = append(opts, "nosuid")
  41. }
  42. if stat.Flags&unix.MNT_UNION != 0 {
  43. opts = append(opts, "union")
  44. }
  45. if stat.Flags&unix.MNT_ASYNC != 0 {
  46. opts = append(opts, "async")
  47. }
  48. if stat.Flags&unix.MNT_SUIDDIR != 0 {
  49. opts = append(opts, "suiddir")
  50. }
  51. if stat.Flags&unix.MNT_SOFTDEP != 0 {
  52. opts = append(opts, "softdep")
  53. }
  54. if stat.Flags&unix.MNT_NOSYMFOLLOW != 0 {
  55. opts = append(opts, "nosymfollow")
  56. }
  57. if stat.Flags&unix.MNT_GJOURNAL != 0 {
  58. opts = append(opts, "gjournal")
  59. }
  60. if stat.Flags&unix.MNT_MULTILABEL != 0 {
  61. opts = append(opts, "multilabel")
  62. }
  63. if stat.Flags&unix.MNT_ACLS != 0 {
  64. opts = append(opts, "acls")
  65. }
  66. if stat.Flags&unix.MNT_NOATIME != 0 {
  67. opts = append(opts, "noatime")
  68. }
  69. if stat.Flags&unix.MNT_NOCLUSTERR != 0 {
  70. opts = append(opts, "noclusterr")
  71. }
  72. if stat.Flags&unix.MNT_NOCLUSTERW != 0 {
  73. opts = append(opts, "noclusterw")
  74. }
  75. if stat.Flags&unix.MNT_NFS4ACLS != 0 {
  76. opts = append(opts, "nfsv4acls")
  77. }
  78. d := PartitionStat{
  79. Device: common.ByteToString(stat.Mntfromname[:]),
  80. Mountpoint: common.ByteToString(stat.Mntonname[:]),
  81. Fstype: common.ByteToString(stat.Fstypename[:]),
  82. Opts: opts,
  83. }
  84. ret = append(ret, d)
  85. }
  86. return ret, nil
  87. }
  88. func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
  89. // statinfo->devinfo->devstat
  90. // /usr/include/devinfo.h
  91. ret := make(map[string]IOCountersStat)
  92. r, err := unix.Sysctl("kern.devstat.all")
  93. if err != nil {
  94. return nil, err
  95. }
  96. buf := []byte(r)
  97. length := len(buf)
  98. count := int(uint64(length) / uint64(sizeOfdevstat))
  99. buf = buf[8:] // devstat.all has version in the head.
  100. // parse buf to devstat
  101. for i := 0; i < count; i++ {
  102. b := buf[i*sizeOfdevstat : i*sizeOfdevstat+sizeOfdevstat]
  103. d, err := parsedevstat(b)
  104. if err != nil {
  105. continue
  106. }
  107. un := strconv.Itoa(int(d.Unit_number))
  108. name := common.IntToString(d.Device_name[:]) + un
  109. if len(names) > 0 && !common.StringsHas(names, name) {
  110. continue
  111. }
  112. ds := IOCountersStat{
  113. ReadCount: d.Operations[devstat_READ],
  114. WriteCount: d.Operations[devstat_WRITE],
  115. ReadBytes: d.Bytes[devstat_READ],
  116. WriteBytes: d.Bytes[devstat_WRITE],
  117. ReadTime: uint64(d.Duration[devstat_READ].Compute() * 1000),
  118. WriteTime: uint64(d.Duration[devstat_WRITE].Compute() * 1000),
  119. IoTime: uint64(d.Busy_time.Compute() * 1000),
  120. Name: name,
  121. }
  122. ds.SerialNumber, _ = SerialNumberWithContext(ctx, name)
  123. ret[name] = ds
  124. }
  125. return ret, nil
  126. }
  127. func (b bintime) Compute() float64 {
  128. BINTIME_SCALE := 5.42101086242752217003726400434970855712890625e-20
  129. return float64(b.Sec) + float64(b.Frac)*BINTIME_SCALE
  130. }
  131. // BT2LD(time) ((long double)(time).sec + (time).frac * BINTIME_SCALE)
  132. func parsedevstat(buf []byte) (devstat, error) {
  133. var ds devstat
  134. br := bytes.NewReader(buf)
  135. // err := binary.Read(br, binary.LittleEndian, &ds)
  136. err := common.Read(br, binary.LittleEndian, &ds)
  137. if err != nil {
  138. return ds, err
  139. }
  140. return ds, nil
  141. }
  142. func getFsType(stat unix.Statfs_t) string {
  143. return common.ByteToString(stat.Fstypename[:])
  144. }
  145. func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
  146. geomOut, err := invoke.CommandWithContext(ctx, "geom", "disk", "list", name)
  147. if err != nil {
  148. return "", fmt.Errorf("exec geom: %w", err)
  149. }
  150. s := bufio.NewScanner(bytes.NewReader(geomOut))
  151. serial := ""
  152. for s.Scan() {
  153. flds := strings.Fields(s.Text())
  154. if len(flds) == 2 && flds[0] == "ident:" {
  155. if flds[1] != "(null)" {
  156. serial = flds[1]
  157. }
  158. break
  159. }
  160. }
  161. if err = s.Err(); err != nil {
  162. return "", err
  163. }
  164. return serial, nil
  165. }
  166. func LabelWithContext(ctx context.Context, name string) (string, error) {
  167. return "", common.ErrNotImplementedError
  168. }