feat(host): pod volume metrics (#21291)

* feat(host): pod volume metrics

* feat(host): add pod_netio for pod
This commit is contained in:
Zexi Li
2024-09-25 14:12:03 +08:00
committed by GitHub
parent 8ba1d316d9
commit ab2dd4db55
32 changed files with 2583 additions and 34 deletions
@@ -264,6 +264,10 @@ func (d disk) doTemplateOverlayAction(
return nil
}
func (d disk) InjectUsageTags(usage *ContainerVolumeMountUsage, vol *hostapi.ContainerVolumeMount) {
usage.Tags["disk_id"] = vol.Disk.Id
}
type diskOverlayDir struct{}
func newDiskOverlayDir() iDiskOverlay {
@@ -17,6 +17,7 @@ package volume_mount
import (
"fmt"
pdisk "github.com/shirou/gopsutil/v3/disk"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
"yunion.io/x/onecloud/pkg/apis"
@@ -55,6 +56,21 @@ type IVolumeMount interface {
Unmount(pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error
}
type ContainerVolumeMountUsage struct {
Id string
HostPath string
MountPath string
VolumeType string
Usage *pdisk.UsageStat
Tags map[string]string
}
type IUsageVolumeMount interface {
IVolumeMount
InjectUsageTags(usage *ContainerVolumeMountUsage, vol *hostapi.ContainerVolumeMount)
}
func GetRuntimeVolumeMountPropagation(input apis.ContainerMountPropagation) runtimeapi.MountPropagation {
switch input {
case apis.MOUNTPROPAGATION_PROPAGATION_PRIVATE:
+4
View File
@@ -108,6 +108,7 @@ func (cr *containerRunner) RunInContainer(pod *desc.SGuestDesc, containerId stri
type PodInstance interface {
GuestRuntimeInstance
GetContainerById(ctrId string) *hostapi.ContainerDesc
CreateContainer(ctx context.Context, userCred mcclient.TokenCredential, id string, input *hostapi.ContainerCreateInput) (jsonutils.JSONObject, error)
StartContainer(ctx context.Context, userCred mcclient.TokenCredential, ctrId string, input *hostapi.ContainerCreateInput) (jsonutils.JSONObject, error)
DeleteContainer(ctx context.Context, cred mcclient.TokenCredential, id string) (jsonutils.JSONObject, error)
@@ -121,6 +122,9 @@ type PodInstance interface {
CommitContainer(ctx context.Context, userCred mcclient.TokenCredential, ctrId string, input *hostapi.ContainerCommitInput) (jsonutils.JSONObject, error)
ReadLogs(ctx context.Context, userCred mcclient.TokenCredential, ctrId string, input *computeapi.PodLogOptions, stdout, stderr io.Writer) error
// for monitoring
GetVolumeMountUsages() (map[ContainerVolumeKey]*volume_mount.ContainerVolumeMountUsage, error)
}
type sContainer struct {
+53
View File
@@ -17,10 +17,14 @@ package guestman
import (
"strings"
"github.com/shirou/gopsutil/v3/disk"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
hostapi "yunion.io/x/onecloud/pkg/apis/host"
"yunion.io/x/onecloud/pkg/hostman/container/volume_mount"
"yunion.io/x/onecloud/pkg/hostman/options"
"yunion.io/x/onecloud/pkg/util/pod/image"
"yunion.io/x/onecloud/pkg/util/pod/nerdctl"
@@ -94,3 +98,52 @@ func PushContainerdImage(input *hostapi.ContainerPushImageInput) error {
}
return nil
}
type ContainerVolumeKey struct {
Id string
HostPath string
}
func (s *sPodGuestInstance) GetVolumeMountUsages() (map[ContainerVolumeKey]*volume_mount.ContainerVolumeMountUsage, error) {
errs := []error{}
result := make(map[ContainerVolumeKey]*volume_mount.ContainerVolumeMountUsage)
for ctrId, vols := range s.getContainerVolumeMounts() {
for i := range vols {
vol := vols[i]
drv, ok := volume_mount.GetDriver(vol.Type).(volume_mount.IUsageVolumeMount)
if !ok {
continue
}
vu, err := s.getVolumeMountUsage(drv, ctrId, vol)
if err != nil {
errs = append(errs, errors.Wrapf(err, "get container %s %s volume usage: %s", ctrId, drv.GetType(), jsonutils.Marshal(vol)))
}
result[ContainerVolumeKey{
Id: ctrId,
HostPath: vu.HostPath,
}] = vu
}
}
return result, errors.NewAggregate(errs)
}
func (s *sPodGuestInstance) getVolumeMountUsage(drv volume_mount.IUsageVolumeMount, ctrId string, vol *hostapi.ContainerVolumeMount) (*volume_mount.ContainerVolumeMountUsage, error) {
hp, err := drv.GetRuntimeMountHostPath(s, ctrId, vol)
if err != nil {
return nil, errors.Wrap(err, "GetRuntimeMountHostPath")
}
us, err := disk.Usage(hp)
if err != nil {
return nil, errors.Wrapf(err, "disk.Usage of %s", hp)
}
usage := &volume_mount.ContainerVolumeMountUsage{
Id: ctrId,
MountPath: vol.MountPath,
HostPath: hp,
VolumeType: string(drv.GetType()),
Usage: us,
Tags: make(map[string]string),
}
drv.InjectUsageTags(usage, vol)
return usage, nil
}
+119 -2
View File
@@ -5,6 +5,9 @@ import (
"strings"
"time"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/hostman/guestman"
"yunion.io/x/onecloud/pkg/util/pod/stats"
)
@@ -17,11 +20,21 @@ const (
MEMORY_WORKING_SET_BYTES = "working_set_bytes"
// memory usage rate
MEMORY_USAGE_RATE = "usage_rate"
VOLUME_TOTAL = "total"
VOLUME_FREE = "free"
VOLUME_USED = "used"
VOLUME_USED_PERCENT = "used_percent"
VOLUME_INODES_TOTAL = "inodes_total"
VOLUME_INODES_FREE = "inodes_free"
VOLUME_INODES_USED = "inodes_used"
VOLUME_INODES_USED_PERCENT = "inodes_used_percent"
)
type PodMetrics struct {
PodCpu *PodCpuMetric `json:"pod_cpu"`
PodMemory *PodMemoryMetric `json:"pod_memory"`
PodVolumes []*PodVolumeMetric `json:"pod_volume"`
Containers []*ContainerMetrics `json:"containers"`
}
@@ -74,6 +87,59 @@ func (m PodMemoryMetric) ToMap() map[string]interface{} {
}
}
type PodVolumeMetric struct {
ContainerMetricMeta
// 容器内挂载路径
MountPath string `json:"mount_path"`
// 宿主机路径
HostPath string `json:"host_path"`
Type string `json:"type"`
Fstype string `json:"fstype"`
Total uint64 `json:"total"`
Free uint64 `json:"free"`
Used uint64 `json:"used"`
UsedPercent float64 `json:"used_percent"`
InodesTotal uint64 `json:"inodes_total"`
InodesUsed uint64 `json:"inodes_used"`
InodesFree uint64 `json:"inodes_free"`
InodesUsedPercent float64 `json:"inodes_used_percent"`
Tags map[string]string `json:"tags"`
}
func (m PodVolumeMetric) GetName() string {
return "pod_volume"
}
func (m PodVolumeMetric) ToMap() map[string]interface{} {
r := map[string]interface{}{
VOLUME_TOTAL: m.Total,
VOLUME_FREE: m.Free,
VOLUME_USED: m.Used,
VOLUME_USED_PERCENT: m.UsedPercent,
VOLUME_INODES_TOTAL: m.InodesTotal,
VOLUME_INODES_FREE: m.InodesFree,
VOLUME_INODES_USED: m.InodesUsed,
VOLUME_INODES_USED_PERCENT: m.InodesUsedPercent,
}
return r
}
func (m PodVolumeMetric) GetTag() map[string]string {
baseTags := m.ContainerMetricMeta.GetTag()
curTags := map[string]string{
"mount_path": m.MountPath,
"host_path": m.HostPath,
"type": m.Type,
}
for k, v := range curTags {
baseTags[k] = v
}
for k, v := range m.Tags {
baseTags[k] = v
}
return baseTags
}
type ContainerMetrics struct {
ContainerCpu *ContainerCpuMetric `json:"container_cpu"`
ContainerMemory *ContainerMemoryMetric `json:"container_memory"`
@@ -147,6 +213,13 @@ func GetPodStatsById(stats []stats.PodStats, podId string) *stats.PodStats {
func (s *SGuestMonitorCollector) collectPodMetrics(gm *SGuestMonitor, prevUsage *GuestMetrics) *GuestMetrics {
gmData := new(GuestMetrics)
gmData.PodMetrics = gm.PodMetrics(prevUsage)
// netio
gmData.VmNetio = gm.Netio()
netio1 := gmData.VmNetio
netio2 := prevUsage.VmNetio
s.addNetio(netio1, netio2)
return gmData
}
@@ -163,6 +236,44 @@ func (m *SGuestMonitor) HasPodMetrics() bool {
return m.podStat != nil
}
func (m *SGuestMonitor) getVolumeMetrics() []*PodVolumeMetric {
pi := m.instance.(guestman.PodInstance)
if !pi.IsRunning() {
return nil
}
vus, err := pi.GetVolumeMountUsages()
if err != nil {
log.Warningf("get volume mount usages: %v", err)
}
result := make([]*PodVolumeMetric, 0)
for i := range vus {
vu := vus[i]
ctr := pi.GetContainerById(vu.Id)
if ctr == nil {
log.Warningf("not found container by %s", vu.Id)
continue
}
meta := NewContainerMetricMeta(pi.GetId(), vu.Id, ctr.Name, time.Now())
result = append(result, &PodVolumeMetric{
ContainerMetricMeta: meta,
MountPath: vu.MountPath,
HostPath: vu.HostPath,
Type: vu.VolumeType,
Fstype: vu.Usage.Fstype,
Total: vu.Usage.Total,
Free: vu.Usage.Free,
Used: vu.Usage.Used,
UsedPercent: vu.Usage.UsedPercent,
InodesTotal: vu.Usage.InodesTotal,
InodesUsed: vu.Usage.InodesUsed,
InodesFree: vu.Usage.InodesFree,
InodesUsedPercent: vu.Usage.InodesUsedPercent,
Tags: vu.Tags,
})
}
return result
}
func (m *SGuestMonitor) PodMetrics(prevUsage *GuestMetrics) *PodMetrics {
stat := m.podStat
podCpu := &PodCpuMetric{
@@ -208,6 +319,7 @@ func (m *SGuestMonitor) PodMetrics(prevUsage *GuestMetrics) *PodMetrics {
return &PodMetrics{
PodCpu: podCpu,
PodMemory: podMemory,
PodVolumes: m.getVolumeMetrics(),
Containers: containers,
}
}
@@ -221,6 +333,9 @@ type iPodMetric interface {
func (d *GuestMetrics) toPodTelegrafData(tagStr string) []string {
m := d.PodMetrics
ims := []iPodMetric{m.PodCpu, m.PodMemory}
for i := range m.PodVolumes {
ims = append(ims, m.PodVolumes[i])
}
for _, c := range m.Containers {
ims = append(ims, c.ContainerCpu)
ims = append(ims, c.ContainerMemory)
@@ -228,14 +343,16 @@ func (d *GuestMetrics) toPodTelegrafData(tagStr string) []string {
res := []string{}
for _, im := range ims {
tagMap := im.GetTag()
newTagStr := tagStr
if len(tagMap) != 0 {
var newTagArr []string
for k, v := range tagMap {
newTagArr = append(newTagArr, fmt.Sprintf("%s=%s", k, v))
}
tagStr = strings.Join([]string{tagStr, strings.Join(newTagArr, ",")}, ",")
newTagStr = strings.Join([]string{tagStr, strings.Join(newTagArr, ",")}, ",")
}
res = append(res, fmt.Sprintf("%s,%s %s", im.GetName(), tagStr, d.mapToStatStr(im.ToMap())))
res = append(res, fmt.Sprintf("%s,%s %s", im.GetName(), newTagStr, d.mapToStatStr(im.ToMap())))
}
res = append(res, d.netioToTelegrafData("pod_netio", tagStr)...)
return res
}
+31 -21
View File
@@ -17,6 +17,7 @@ package hostmetrics
import (
"context"
"fmt"
"io"
"runtime/debug"
"strconv"
"strings"
@@ -111,7 +112,8 @@ func (m *SHostMetricsCollector) reportUsageToTelegraf(data string) {
}
defer res.Body.Close()
if res.StatusCode != 204 {
log.Errorf("upload guest metric failed %d", res.StatusCode)
resBody, _ := io.ReadAll(res.Body)
log.Errorf("upload guest metric failed %d: %s", res.StatusCode, string(resBody))
timestamp := time.Now().UnixNano()
for _, line := range strings.Split(data, "\n") {
m.waitingReportData = append(m.waitingReportData,
@@ -199,7 +201,7 @@ func (s *SGuestMonitorCollector) GetGuests() map[string]*SGuestMonitor {
gm.MemMB = instance.GetDesc().Mem
} else {
delete(s.monitors, guestId)
gm, err = NewGuestMonitor(guestName, guestId, pid, nicsDesc, int(vcpuCount))
gm, err = NewGuestMonitor(instance, guestName, guestId, pid, nicsDesc, int(vcpuCount))
if err != nil {
log.Errorf("NewGuestMonitor for %s(%s), pid: %d, nics: %#v", guestName, guestId, pid, nicsDesc)
return true
@@ -225,7 +227,7 @@ func (s *SGuestMonitorCollector) GetGuests() map[string]*SGuestMonitor {
}
podStat := GetPodStatsById(podStats, guestId)
if podStat != nil {
gm, err := NewGuestPodMonitor(guestName, guestId, podStat, nicsDesc, int(vcpuCount))
gm, err := NewGuestPodMonitor(instance, guestName, guestId, podStat, nicsDesc, int(vcpuCount))
if err != nil {
return true
}
@@ -409,21 +411,27 @@ func (d *GuestMetrics) mapToStatStr(m map[string]interface{}) string {
return strings.Join(statArr, ",")
}
func (d *GuestMetrics) toVmTelegrafData(tagStr string) []string {
var res = []string{}
res = append(res, fmt.Sprintf("%s,%s %s", "vm_cpu", tagStr, d.mapToStatStr(d.VmCpu.ToMap())))
res = append(res, fmt.Sprintf("%s,%s %s", "vm_mem", tagStr, d.mapToStatStr(d.VmMem.ToMap())))
res = append(res, fmt.Sprintf("%s,%s %s", "vm_diskio", tagStr, d.mapToStatStr(d.VmDiskio.ToMap())))
func (d *GuestMetrics) netioToTelegrafData(measurement string, tagStr string) []string {
res := []string{}
for i := range d.VmNetio {
netTagMap := d.VmNetio[i].ToTag()
for k, v := range netTagMap {
tagStr = fmt.Sprintf("%s,%s=%s", tagStr, k, v)
}
res = append(res, fmt.Sprintf("%s,%s %s", "vm_netio", tagStr, d.mapToStatStr(d.VmNetio[i].ToMap())))
res = append(res, fmt.Sprintf("%s,%s %s", measurement, tagStr, d.mapToStatStr(d.VmNetio[i].ToMap())))
}
return res
}
func (d *GuestMetrics) toVmTelegrafData(tagStr string) []string {
var res = []string{}
res = append(res, fmt.Sprintf("%s,%s %s", "vm_cpu", tagStr, d.mapToStatStr(d.VmCpu.ToMap())))
res = append(res, fmt.Sprintf("%s,%s %s", "vm_mem", tagStr, d.mapToStatStr(d.VmMem.ToMap())))
res = append(res, fmt.Sprintf("%s,%s %s", "vm_diskio", tagStr, d.mapToStatStr(d.VmDiskio.ToMap())))
res = append(res, d.netioToTelegrafData("vm_netio", tagStr)...)
return res
}
func (d *GuestMetrics) toTelegrafData(tags map[string]string) []string {
var tagArr = []string{}
for k, v := range tags {
@@ -544,18 +552,19 @@ type SGuestMonitor struct {
DomainId string
ProjectDomain string
podStat *stats.PodStats
instance guestman.GuestRuntimeInstance
}
func NewGuestMonitor(name, id string, pid int, nics []*desc.SGuestNetwork, cpuCount int) (*SGuestMonitor, error) {
func NewGuestMonitor(instance guestman.GuestRuntimeInstance, name, id string, pid int, nics []*desc.SGuestNetwork, cpuCount int) (*SGuestMonitor, error) {
proc, err := process.NewProcess(int32(pid))
if err != nil {
return nil, err
}
return newGuestMonitor(name, id, proc, nics, cpuCount)
return newGuestMonitor(instance, name, id, proc, nics, cpuCount)
}
func NewGuestPodMonitor(name, id string, stat *stats.PodStats, nics []*desc.SGuestNetwork, cpuCount int) (*SGuestMonitor, error) {
m, err := newGuestMonitor(name, id, nil, nics, cpuCount)
func NewGuestPodMonitor(instance guestman.GuestRuntimeInstance, name, id string, stat *stats.PodStats, nics []*desc.SGuestNetwork, cpuCount int) (*SGuestMonitor, error) {
m, err := newGuestMonitor(instance, name, id, nil, nics, cpuCount)
if err != nil {
return nil, errors.Wrap(err, "new pod GuestMonitor")
}
@@ -563,7 +572,7 @@ func NewGuestPodMonitor(name, id string, stat *stats.PodStats, nics []*desc.SGue
return m, nil
}
func newGuestMonitor(name, id string, proc *process.Process, nics []*desc.SGuestNetwork, cpuCount int) (*SGuestMonitor, error) {
func newGuestMonitor(instance guestman.GuestRuntimeInstance, name, id string, proc *process.Process, nics []*desc.SGuestNetwork, cpuCount int) (*SGuestMonitor, error) {
var ip string
if len(nics) >= 1 {
ip = nics[0].Ip
@@ -573,13 +582,14 @@ func newGuestMonitor(name, id string, proc *process.Process, nics []*desc.SGuest
pid = int(proc.Pid)
}
return &SGuestMonitor{
Name: name,
Id: id,
Pid: pid,
Nics: nics,
CpuCnt: cpuCount,
Ip: ip,
Process: proc,
Name: name,
Id: id,
Pid: pid,
Nics: nics,
CpuCnt: cpuCount,
Ip: ip,
Process: proc,
instance: instance,
}, nil
}
+27 -11
View File
@@ -65,6 +65,16 @@ func newMetricFieldCreateInput(name, displayName, unit string, score int) monito
}
}
func registryNetio(measurement string, displayName string, resType string, score int) {
RegistryMetricCreateInput(measurement, displayName, resType,
monitor.METRIC_DATABASE_TELE, score, []monitor.MetricFieldCreateInput{
newMetricFieldCreateInput("bps_recv", "Received traffic per second", monitor.METRIC_UNIT_BPS, 1),
newMetricFieldCreateInput("bps_sent", "Send traffic per second", monitor.METRIC_UNIT_BPS, 2),
newMetricFieldCreateInput("pps_recv", "Received packets per second", monitor.METRIC_UNIT_PPS, 3),
newMetricFieldCreateInput("pps_sent", "Send packets per second", monitor.METRIC_UNIT_PPS, 4),
})
}
// order by score asc
// score default:99
func init() {
@@ -180,13 +190,7 @@ func init() {
})
// vm_netio
RegistryMetricCreateInput("vm_netio", "Guest network traffic", monitor.METRIC_RES_TYPE_GUEST,
monitor.METRIC_DATABASE_TELE, 4, []monitor.MetricFieldCreateInput{
newMetricFieldCreateInput("bps_recv", "Received traffic per second", monitor.METRIC_UNIT_BPS, 1),
newMetricFieldCreateInput("bps_sent", "Send traffic per second", monitor.METRIC_UNIT_BPS, 2),
newMetricFieldCreateInput("pps_recv", "Received packets per second", monitor.METRIC_UNIT_PPS, 3),
newMetricFieldCreateInput("pps_sent", "Send packets per second", monitor.METRIC_UNIT_PPS, 4),
})
registryNetio("vm_netio", "Guest network traffic", monitor.METRIC_RES_TYPE_GUEST, 4)
// oss_latency
RegistryMetricCreateInput("oss_latency", "Object storage latency",
@@ -502,19 +506,31 @@ func init() {
// metrics of pod and container
RegistryMetricCreateInput("pod_cpu", "Pod cpu", monitor.METRIC_RES_TYPE_CONTAINER,
monitor.METRIC_DATABASE_TELE, 3, []monitor.MetricFieldCreateInput{
monitor.METRIC_DATABASE_TELE, 4, []monitor.MetricFieldCreateInput{
newMetricFieldCreateInput("usage_rate", "Pod cpu usage rate", monitor.METRIC_UNIT_PERCENT, 1),
})
RegistryMetricCreateInput("pod_mem", "Pod memory", monitor.METRIC_RES_TYPE_CONTAINER, monitor.METRIC_DATABASE_TELE, 3, []monitor.MetricFieldCreateInput{
RegistryMetricCreateInput("pod_mem", "Pod memory", monitor.METRIC_RES_TYPE_CONTAINER, monitor.METRIC_DATABASE_TELE, 5, []monitor.MetricFieldCreateInput{
newMetricFieldCreateInput("usage_rate", "Pod memory usage rate", monitor.METRIC_UNIT_PERCENT, 1),
newMetricFieldCreateInput("working_set_bytes", "Pod memory working set bytes", monitor.METRIC_UNIT_BYTE, 2),
})
RegistryMetricCreateInput("pod_volume", "Pod volume",
monitor.METRIC_RES_TYPE_CONTAINER, monitor.METRIC_DATABASE_TELE, 6, []monitor.MetricFieldCreateInput{
newMetricFieldCreateInput("total", "Pod volume total size", monitor.METRIC_UNIT_BYTE, 1),
newMetricFieldCreateInput("free", "Pod volume free size", monitor.METRIC_UNIT_BYTE, 2),
newMetricFieldCreateInput("used", "Pod volume used size", monitor.METRIC_UNIT_BYTE, 3),
newMetricFieldCreateInput("used_percent", "Pod volume used percent", monitor.METRIC_UNIT_PERCENT, 4),
newMetricFieldCreateInput("inodes_total", "Pod volume inodes total count", monitor.METRIC_UNIT_COUNT, 5),
newMetricFieldCreateInput("inodes_free", "Pod volume inodes free count", monitor.METRIC_UNIT_COUNT, 6),
newMetricFieldCreateInput("inodes_used", "Pod volume inodes used count", monitor.METRIC_UNIT_COUNT, 7),
newMetricFieldCreateInput("inodes_used_percent", "Pod volume inodes used percent", monitor.METRIC_UNIT_PERCENT, 8),
})
registryNetio("pod_netio", "Pod network traffic", monitor.METRIC_RES_TYPE_CONTAINER, 7)
RegistryMetricCreateInput("container_cpu", "Container cpu", monitor.METRIC_RES_TYPE_CONTAINER,
monitor.METRIC_DATABASE_TELE, 3, []monitor.MetricFieldCreateInput{
monitor.METRIC_DATABASE_TELE, 8, []monitor.MetricFieldCreateInput{
newMetricFieldCreateInput("usage_rate", "Container cpu usage rate", monitor.METRIC_UNIT_PERCENT, 1),
})
RegistryMetricCreateInput("container_mem", "Container memory", monitor.METRIC_RES_TYPE_CONTAINER, monitor.METRIC_DATABASE_TELE, 3, []monitor.MetricFieldCreateInput{
RegistryMetricCreateInput("container_mem", "Container memory", monitor.METRIC_RES_TYPE_CONTAINER, monitor.METRIC_DATABASE_TELE, 9, []monitor.MetricFieldCreateInput{
newMetricFieldCreateInput("usage_rate", "Container memory usage rate", monitor.METRIC_UNIT_PERCENT, 1),
newMetricFieldCreateInput("working_set_bytes", "Container memory working set bytes", monitor.METRIC_UNIT_BYTE, 2),
})
+96
View File
@@ -0,0 +1,96 @@
package disk
import (
"context"
"encoding/json"
"github.com/shirou/gopsutil/v3/internal/common"
)
var invoke common.Invoker = common.Invoke{}
type UsageStat struct {
Path string `json:"path"`
Fstype string `json:"fstype"`
Total uint64 `json:"total"`
Free uint64 `json:"free"`
Used uint64 `json:"used"`
UsedPercent float64 `json:"usedPercent"`
InodesTotal uint64 `json:"inodesTotal"`
InodesUsed uint64 `json:"inodesUsed"`
InodesFree uint64 `json:"inodesFree"`
InodesUsedPercent float64 `json:"inodesUsedPercent"`
}
type PartitionStat struct {
Device string `json:"device"`
Mountpoint string `json:"mountpoint"`
Fstype string `json:"fstype"`
Opts []string `json:"opts"`
}
type IOCountersStat struct {
ReadCount uint64 `json:"readCount"`
MergedReadCount uint64 `json:"mergedReadCount"`
WriteCount uint64 `json:"writeCount"`
MergedWriteCount uint64 `json:"mergedWriteCount"`
ReadBytes uint64 `json:"readBytes"`
WriteBytes uint64 `json:"writeBytes"`
ReadTime uint64 `json:"readTime"`
WriteTime uint64 `json:"writeTime"`
IopsInProgress uint64 `json:"iopsInProgress"`
IoTime uint64 `json:"ioTime"`
WeightedIO uint64 `json:"weightedIO"`
Name string `json:"name"`
SerialNumber string `json:"serialNumber"`
Label string `json:"label"`
}
func (d UsageStat) String() string {
s, _ := json.Marshal(d)
return string(s)
}
func (d PartitionStat) String() string {
s, _ := json.Marshal(d)
return string(s)
}
func (d IOCountersStat) String() string {
s, _ := json.Marshal(d)
return string(s)
}
// Usage returns a file system usage. path is a filesystem path such
// as "/", not device file path like "/dev/vda1". If you want to use
// a return value of disk.Partitions, use "Mountpoint" not "Device".
func Usage(path string) (*UsageStat, error) {
return UsageWithContext(context.Background(), path)
}
// Partitions returns disk partitions. If all is false, returns
// physical devices only (e.g. hard disks, cd-rom drives, USB keys)
// and ignore all others (e.g. memory partitions such as /dev/shm)
//
// 'all' argument is ignored for BSD, see: https://github.com/giampaolo/psutil/issues/906
func Partitions(all bool) ([]PartitionStat, error) {
return PartitionsWithContext(context.Background(), all)
}
func IOCounters(names ...string) (map[string]IOCountersStat, error) {
return IOCountersWithContext(context.Background(), names...)
}
// SerialNumber returns Serial Number of given device or empty string
// on error. Name of device is expected, eg. /dev/sda
func SerialNumber(name string) (string, error) {
return SerialNumberWithContext(context.Background(), name)
}
// Label returns label of given device or empty string on error.
// Name of device is expected, eg. /dev/sda
// Supports label based on devicemapper name
// See https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-block-dm
func Label(name string) (string, error) {
return LabelWithContext(context.Background(), name)
}
+22
View File
@@ -0,0 +1,22 @@
//go:build aix
// +build aix
package disk
import (
"context"
"github.com/shirou/gopsutil/v3/internal/common"
)
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
return nil, common.ErrNotImplementedError
}
func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
return "", common.ErrNotImplementedError
}
func LabelWithContext(ctx context.Context, name string) (string, error) {
return "", common.ErrNotImplementedError
}
+76
View File
@@ -0,0 +1,76 @@
//go:build aix && cgo
// +build aix,cgo
package disk
import (
"context"
"fmt"
"github.com/power-devops/perfstat"
)
var FSType map[int]string
func init() {
FSType = map[int]string{
0: "jfs2", 1: "namefs", 2: "nfs", 3: "jfs", 5: "cdrom", 6: "proc",
16: "special-fs", 17: "cache-fs", 18: "nfs3", 19: "automount-fs", 20: "pool-fs", 32: "vxfs",
33: "veritas-fs", 34: "udfs", 35: "nfs4", 36: "nfs4-pseudo", 37: "smbfs", 38: "mcr-pseudofs",
39: "ahafs", 40: "sterm-nfs", 41: "asmfs",
}
}
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
f, err := perfstat.FileSystemStat()
if err != nil {
return nil, err
}
ret := make([]PartitionStat, len(f))
for _, fs := range f {
fstyp, exists := FSType[fs.FSType]
if !exists {
fstyp = "unknown"
}
info := PartitionStat{
Device: fs.Device,
Mountpoint: fs.MountPoint,
Fstype: fstyp,
}
ret = append(ret, info)
}
return ret, err
}
func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
f, err := perfstat.FileSystemStat()
if err != nil {
return nil, err
}
blocksize := uint64(512)
for _, fs := range f {
if path == fs.MountPoint {
fstyp, exists := FSType[fs.FSType]
if !exists {
fstyp = "unknown"
}
info := UsageStat{
Path: path,
Fstype: fstyp,
Total: uint64(fs.TotalBlocks) * blocksize,
Free: uint64(fs.FreeBlocks) * blocksize,
Used: uint64(fs.TotalBlocks-fs.FreeBlocks) * blocksize,
InodesTotal: uint64(fs.TotalInodes),
InodesFree: uint64(fs.FreeInodes),
InodesUsed: uint64(fs.TotalInodes - fs.FreeInodes),
}
info.UsedPercent = (float64(info.Used) / float64(info.Total)) * 100.0
info.InodesUsedPercent = (float64(info.InodesUsed) / float64(info.InodesTotal)) * 100.0
return &info, nil
}
}
return nil, fmt.Errorf("mountpoint %s not found", path)
}
+82
View File
@@ -0,0 +1,82 @@
//go:build aix && !cgo
// +build aix,!cgo
package disk
import (
"context"
"regexp"
"strings"
"golang.org/x/sys/unix"
"github.com/shirou/gopsutil/v3/internal/common"
)
var whiteSpaces = regexp.MustCompile(`\s+`)
var startBlank = regexp.MustCompile(`^\s+`)
var ignoreFSType = map[string]bool{"procfs": true}
var FSType = map[int]string{
0: "jfs2", 1: "namefs", 2: "nfs", 3: "jfs", 5: "cdrom", 6: "proc",
16: "special-fs", 17: "cache-fs", 18: "nfs3", 19: "automount-fs", 20: "pool-fs", 32: "vxfs",
33: "veritas-fs", 34: "udfs", 35: "nfs4", 36: "nfs4-pseudo", 37: "smbfs", 38: "mcr-pseudofs",
39: "ahafs", 40: "sterm-nfs", 41: "asmfs",
}
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
var ret []PartitionStat
out, err := invoke.CommandWithContext(ctx, "mount")
if err != nil {
return nil, err
}
// parse head lines for column names
colidx := make(map[string]int)
lines := strings.Split(string(out), "\n")
if len(lines) < 3 {
return nil, common.ErrNotImplementedError
}
idx := 0
start := 0
finished := false
for pos, ch := range lines[1] {
if ch == ' ' && ! finished {
name := strings.TrimSpace(lines[0][start:pos])
colidx[name] = idx
finished = true
} else if ch == '-' && finished {
idx++
start = pos
finished = false
}
}
name := strings.TrimSpace(lines[0][start:len(lines[1])])
colidx[name] = idx
for idx := 2; idx < len(lines); idx++ {
line := lines[idx]
if startBlank.MatchString(line) {
line = "localhost" + line
}
p := whiteSpaces.Split(lines[idx], 6)
if len(p) < 5 || ignoreFSType[p[colidx["vfs"]]] {
continue
}
d := PartitionStat{
Device: p[colidx["mounted"]],
Mountpoint: p[colidx["mounted over"]],
Fstype: p[colidx["vfs"]],
Opts: strings.Split(p[colidx["options"]], ","),
}
ret = append(ret, d)
}
return ret, nil
}
func getFsType(stat unix.Statfs_t) string {
return FSType[int(stat.Vfstype)]
}
+87
View File
@@ -0,0 +1,87 @@
//go:build darwin
// +build darwin
package disk
import (
"context"
"github.com/shirou/gopsutil/v3/internal/common"
"golang.org/x/sys/unix"
)
// PartitionsWithContext returns disk partition.
// 'all' argument is ignored, see: https://github.com/giampaolo/psutil/issues/906
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
var ret []PartitionStat
count, err := unix.Getfsstat(nil, unix.MNT_WAIT)
if err != nil {
return ret, err
}
fs := make([]unix.Statfs_t, count)
if _, err = unix.Getfsstat(fs, unix.MNT_WAIT); err != nil {
return ret, err
}
for _, stat := range fs {
opts := []string{"rw"}
if stat.Flags&unix.MNT_RDONLY != 0 {
opts = []string{"ro"}
}
if stat.Flags&unix.MNT_SYNCHRONOUS != 0 {
opts = append(opts, "sync")
}
if stat.Flags&unix.MNT_NOEXEC != 0 {
opts = append(opts, "noexec")
}
if stat.Flags&unix.MNT_NOSUID != 0 {
opts = append(opts, "nosuid")
}
if stat.Flags&unix.MNT_UNION != 0 {
opts = append(opts, "union")
}
if stat.Flags&unix.MNT_ASYNC != 0 {
opts = append(opts, "async")
}
if stat.Flags&unix.MNT_DONTBROWSE != 0 {
opts = append(opts, "nobrowse")
}
if stat.Flags&unix.MNT_AUTOMOUNTED != 0 {
opts = append(opts, "automounted")
}
if stat.Flags&unix.MNT_JOURNALED != 0 {
opts = append(opts, "journaled")
}
if stat.Flags&unix.MNT_MULTILABEL != 0 {
opts = append(opts, "multilabel")
}
if stat.Flags&unix.MNT_NOATIME != 0 {
opts = append(opts, "noatime")
}
if stat.Flags&unix.MNT_NODEV != 0 {
opts = append(opts, "nodev")
}
d := PartitionStat{
Device: common.ByteToString(stat.Mntfromname[:]),
Mountpoint: common.ByteToString(stat.Mntonname[:]),
Fstype: common.ByteToString(stat.Fstypename[:]),
Opts: opts,
}
ret = append(ret, d)
}
return ret, nil
}
func getFsType(stat unix.Statfs_t) string {
return common.ByteToString(stat.Fstypename[:])
}
func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
return "", common.ErrNotImplementedError
}
func LabelWithContext(ctx context.Context, name string) (string, error) {
return "", common.ErrNotImplementedError
}
+45
View File
@@ -0,0 +1,45 @@
//go:build darwin && cgo
// +build darwin,cgo
package disk
/*
#cgo LDFLAGS: -framework CoreFoundation -framework IOKit
#include <stdint.h>
#include <CoreFoundation/CoreFoundation.h>
#include "iostat_darwin.h"
*/
import "C"
import (
"context"
"github.com/shirou/gopsutil/v3/internal/common"
)
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
var buf [C.NDRIVE]C.DriveStats
n, err := C.gopsutil_v3_readdrivestat(&buf[0], C.int(len(buf)))
if err != nil {
return nil, err
}
ret := make(map[string]IOCountersStat, 0)
for i := 0; i < int(n); i++ {
d := IOCountersStat{
ReadBytes: uint64(buf[i].read),
WriteBytes: uint64(buf[i].written),
ReadCount: uint64(buf[i].nread),
WriteCount: uint64(buf[i].nwrite),
ReadTime: uint64(buf[i].readtime / 1000 / 1000), // note: read/write time are in ns, but we want ms.
WriteTime: uint64(buf[i].writetime / 1000 / 1000),
IoTime: uint64((buf[i].readtime + buf[i].writetime) / 1000 / 1000),
Name: C.GoString(&buf[i].name[0]),
}
if len(names) > 0 && !common.StringsHas(names, d.Name) {
continue
}
ret[d.Name] = d
}
return ret, nil
}
+14
View File
@@ -0,0 +1,14 @@
//go:build darwin && !cgo
// +build darwin,!cgo
package disk
import (
"context"
"github.com/shirou/gopsutil/v3/internal/common"
)
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
return nil, common.ErrNotImplementedError
}
+30
View File
@@ -0,0 +1,30 @@
//go:build !darwin && !linux && !freebsd && !openbsd && !windows && !solaris && !aix
// +build !darwin,!linux,!freebsd,!openbsd,!windows,!solaris,!aix
package disk
import (
"context"
"github.com/shirou/gopsutil/v3/internal/common"
)
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
return nil, common.ErrNotImplementedError
}
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
return []PartitionStat{}, common.ErrNotImplementedError
}
func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
return nil, common.ErrNotImplementedError
}
func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
return "", common.ErrNotImplementedError
}
func LabelWithContext(ctx context.Context, name string) (string, error) {
return "", common.ErrNotImplementedError
}
+193
View File
@@ -0,0 +1,193 @@
//go:build freebsd
// +build freebsd
package disk
import (
"bufio"
"bytes"
"context"
"encoding/binary"
"fmt"
"strconv"
"strings"
"golang.org/x/sys/unix"
"github.com/shirou/gopsutil/v3/internal/common"
)
// PartitionsWithContext returns disk partition.
// 'all' argument is ignored, see: https://github.com/giampaolo/psutil/issues/906
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
var ret []PartitionStat
// get length
count, err := unix.Getfsstat(nil, unix.MNT_WAIT)
if err != nil {
return ret, err
}
fs := make([]unix.Statfs_t, count)
if _, err = unix.Getfsstat(fs, unix.MNT_WAIT); err != nil {
return ret, err
}
for _, stat := range fs {
opts := []string{"rw"}
if stat.Flags&unix.MNT_RDONLY != 0 {
opts = []string{"ro"}
}
if stat.Flags&unix.MNT_SYNCHRONOUS != 0 {
opts = append(opts, "sync")
}
if stat.Flags&unix.MNT_NOEXEC != 0 {
opts = append(opts, "noexec")
}
if stat.Flags&unix.MNT_NOSUID != 0 {
opts = append(opts, "nosuid")
}
if stat.Flags&unix.MNT_UNION != 0 {
opts = append(opts, "union")
}
if stat.Flags&unix.MNT_ASYNC != 0 {
opts = append(opts, "async")
}
if stat.Flags&unix.MNT_SUIDDIR != 0 {
opts = append(opts, "suiddir")
}
if stat.Flags&unix.MNT_SOFTDEP != 0 {
opts = append(opts, "softdep")
}
if stat.Flags&unix.MNT_NOSYMFOLLOW != 0 {
opts = append(opts, "nosymfollow")
}
if stat.Flags&unix.MNT_GJOURNAL != 0 {
opts = append(opts, "gjournal")
}
if stat.Flags&unix.MNT_MULTILABEL != 0 {
opts = append(opts, "multilabel")
}
if stat.Flags&unix.MNT_ACLS != 0 {
opts = append(opts, "acls")
}
if stat.Flags&unix.MNT_NOATIME != 0 {
opts = append(opts, "noatime")
}
if stat.Flags&unix.MNT_NOCLUSTERR != 0 {
opts = append(opts, "noclusterr")
}
if stat.Flags&unix.MNT_NOCLUSTERW != 0 {
opts = append(opts, "noclusterw")
}
if stat.Flags&unix.MNT_NFS4ACLS != 0 {
opts = append(opts, "nfsv4acls")
}
d := PartitionStat{
Device: common.ByteToString(stat.Mntfromname[:]),
Mountpoint: common.ByteToString(stat.Mntonname[:]),
Fstype: common.ByteToString(stat.Fstypename[:]),
Opts: opts,
}
ret = append(ret, d)
}
return ret, nil
}
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
// statinfo->devinfo->devstat
// /usr/include/devinfo.h
ret := make(map[string]IOCountersStat)
r, err := unix.Sysctl("kern.devstat.all")
if err != nil {
return nil, err
}
buf := []byte(r)
length := len(buf)
count := int(uint64(length) / uint64(sizeOfdevstat))
buf = buf[8:] // devstat.all has version in the head.
// parse buf to devstat
for i := 0; i < count; i++ {
b := buf[i*sizeOfdevstat : i*sizeOfdevstat+sizeOfdevstat]
d, err := parsedevstat(b)
if err != nil {
continue
}
un := strconv.Itoa(int(d.Unit_number))
name := common.IntToString(d.Device_name[:]) + un
if len(names) > 0 && !common.StringsHas(names, name) {
continue
}
ds := IOCountersStat{
ReadCount: d.Operations[devstat_READ],
WriteCount: d.Operations[devstat_WRITE],
ReadBytes: d.Bytes[devstat_READ],
WriteBytes: d.Bytes[devstat_WRITE],
ReadTime: uint64(d.Duration[devstat_READ].Compute() * 1000),
WriteTime: uint64(d.Duration[devstat_WRITE].Compute() * 1000),
IoTime: uint64(d.Busy_time.Compute() * 1000),
Name: name,
}
ds.SerialNumber, _ = SerialNumberWithContext(ctx, name)
ret[name] = ds
}
return ret, nil
}
func (b bintime) Compute() float64 {
BINTIME_SCALE := 5.42101086242752217003726400434970855712890625e-20
return float64(b.Sec) + float64(b.Frac)*BINTIME_SCALE
}
// BT2LD(time) ((long double)(time).sec + (time).frac * BINTIME_SCALE)
func parsedevstat(buf []byte) (devstat, error) {
var ds devstat
br := bytes.NewReader(buf)
// err := binary.Read(br, binary.LittleEndian, &ds)
err := common.Read(br, binary.LittleEndian, &ds)
if err != nil {
return ds, err
}
return ds, nil
}
func getFsType(stat unix.Statfs_t) string {
return common.ByteToString(stat.Fstypename[:])
}
func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
geomOut, err := invoke.CommandWithContext(ctx, "geom", "disk", "list", name)
if err != nil {
return "", fmt.Errorf("exec geom: %w", err)
}
s := bufio.NewScanner(bytes.NewReader(geomOut))
serial := ""
for s.Scan() {
flds := strings.Fields(s.Text())
if len(flds) == 2 && flds[0] == "ident:" {
if flds[1] != "(null)" {
serial = flds[1]
}
break
}
}
if err = s.Err(); err != nil {
return "", err
}
return serial, nil
}
func LabelWithContext(ctx context.Context, name string) (string, error) {
return "", common.ErrNotImplementedError
}
+63
View File
@@ -0,0 +1,63 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_freebsd.go
package disk
const (
sizeofPtr = 0x4
sizeofShort = 0x2
sizeofInt = 0x4
sizeofLong = 0x4
sizeofLongLong = 0x8
sizeofLongDouble = 0x8
devstat_NO_DATA = 0x00
devstat_READ = 0x01
devstat_WRITE = 0x02
devstat_FREE = 0x03
)
const (
sizeOfdevstat = 0xf0
)
type (
_C_short int16
_C_int int32
_C_long int32
_C_long_long int64
_C_long_double int64
)
type devstat struct {
Sequence0 uint32
Allocated int32
Start_count uint32
End_count uint32
Busy_from bintime
Dev_links _Ctype_struct___0
Device_number uint32
Device_name [16]int8
Unit_number int32
Bytes [4]uint64
Operations [4]uint64
Duration [4]bintime
Busy_time bintime
Creation_time bintime
Block_size uint32
Tag_types [3]uint64
Flags uint32
Device_type uint32
Priority uint32
Id *byte
Sequence1 uint32
}
type bintime struct {
Sec int32
Frac uint64
}
type _Ctype_struct___0 struct {
Empty uint32
}
+66
View File
@@ -0,0 +1,66 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_freebsd.go
package disk
const (
sizeofPtr = 0x8
sizeofShort = 0x2
sizeofInt = 0x4
sizeofLong = 0x8
sizeofLongLong = 0x8
sizeofLongDouble = 0x8
devstat_NO_DATA = 0x00
devstat_READ = 0x01
devstat_WRITE = 0x02
devstat_FREE = 0x03
)
const (
sizeOfdevstat = 0x120
)
type (
_C_short int16
_C_int int32
_C_long int64
_C_long_long int64
_C_long_double int64
)
type devstat struct {
Sequence0 uint32
Allocated int32
Start_count uint32
End_count uint32
Busy_from bintime
Dev_links _Ctype_struct___0
Device_number uint32
Device_name [16]int8
Unit_number int32
Bytes [4]uint64
Operations [4]uint64
Duration [4]bintime
Busy_time bintime
Creation_time bintime
Block_size uint32
Pad_cgo_0 [4]byte
Tag_types [3]uint64
Flags uint32
Device_type uint32
Priority uint32
Pad_cgo_1 [4]byte
ID *byte
Sequence1 uint32
Pad_cgo_2 [4]byte
}
type bintime struct {
Sec int64
Frac uint64
}
type _Ctype_struct___0 struct {
Empty uint64
}
+63
View File
@@ -0,0 +1,63 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_freebsd.go
package disk
const (
sizeofPtr = 0x4
sizeofShort = 0x2
sizeofInt = 0x4
sizeofLong = 0x4
sizeofLongLong = 0x8
sizeofLongDouble = 0x8
devstat_NO_DATA = 0x00
devstat_READ = 0x01
devstat_WRITE = 0x02
devstat_FREE = 0x03
)
const (
sizeOfdevstat = 0xf0
)
type (
_C_short int16
_C_int int32
_C_long int32
_C_long_long int64
_C_long_double int64
)
type devstat struct {
Sequence0 uint32
Allocated int32
Start_count uint32
End_count uint32
Busy_from bintime
Dev_links _Ctype_struct___0
Device_number uint32
Device_name [16]int8
Unit_number int32
Bytes [4]uint64
Operations [4]uint64
Duration [4]bintime
Busy_time bintime
Creation_time bintime
Block_size uint32
Tag_types [3]uint64
Flags uint32
Device_type uint32
Priority uint32
Id *byte
Sequence1 uint32
}
type bintime struct {
Sec int32
Frac uint64
}
type _Ctype_struct___0 struct {
Empty uint32
}
+66
View File
@@ -0,0 +1,66 @@
//go:build freebsd && arm64
// +build freebsd,arm64
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
// cgo -godefs disk/types_freebsd.go
package disk
const (
sizeofPtr = 0x8
sizeofShort = 0x2
sizeofInt = 0x4
sizeofLong = 0x8
sizeofLongLong = 0x8
sizeofLongDouble = 0x8
devstat_NO_DATA = 0x00
devstat_READ = 0x01
devstat_WRITE = 0x02
devstat_FREE = 0x03
)
const (
sizeOfdevstat = 0x120
)
type (
_C_short int16
_C_int int32
_C_long int64
_C_long_long int64
_C_long_double int64
)
type devstat struct {
Sequence0 uint32
Allocated int32
Start_count uint32
End_count uint32
Busy_from bintime
Dev_links _Ctype_struct___0
Device_number uint32
Device_name [16]int8
Unit_number int32
Bytes [4]uint64
Operations [4]uint64
Duration [4]bintime
Busy_time bintime
Creation_time bintime
Block_size uint32
Tag_types [3]uint64
Flags uint32
Device_type uint32
Priority uint32
Id *byte
Sequence1 uint32
Pad_cgo_0 [4]byte
}
type bintime struct {
Sec int64
Frac uint64
}
type _Ctype_struct___0 struct {
Empty uint64
}
+538
View File
@@ -0,0 +1,538 @@
//go:build linux
// +build linux
package disk
import (
"bufio"
"bytes"
"context"
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"github.com/shirou/gopsutil/v3/internal/common"
"golang.org/x/sys/unix"
)
const (
sectorSize = 512
)
const (
// man statfs
ADFS_SUPER_MAGIC = 0xadf5
AFFS_SUPER_MAGIC = 0xADFF
BDEVFS_MAGIC = 0x62646576
BEFS_SUPER_MAGIC = 0x42465331
BFS_MAGIC = 0x1BADFACE
BINFMTFS_MAGIC = 0x42494e4d
BTRFS_SUPER_MAGIC = 0x9123683E
CGROUP_SUPER_MAGIC = 0x27e0eb
CIFS_MAGIC_NUMBER = 0xFF534D42
CODA_SUPER_MAGIC = 0x73757245
COH_SUPER_MAGIC = 0x012FF7B7
CRAMFS_MAGIC = 0x28cd3d45
DEBUGFS_MAGIC = 0x64626720
DEVFS_SUPER_MAGIC = 0x1373
DEVPTS_SUPER_MAGIC = 0x1cd1
EFIVARFS_MAGIC = 0xde5e81e4
EFS_SUPER_MAGIC = 0x00414A53
EXT_SUPER_MAGIC = 0x137D
EXT2_OLD_SUPER_MAGIC = 0xEF51
EXT2_SUPER_MAGIC = 0xEF53
EXT3_SUPER_MAGIC = 0xEF53
EXT4_SUPER_MAGIC = 0xEF53
FUSE_SUPER_MAGIC = 0x65735546
FUTEXFS_SUPER_MAGIC = 0xBAD1DEA
HFS_SUPER_MAGIC = 0x4244
HFSPLUS_SUPER_MAGIC = 0x482b
HOSTFS_SUPER_MAGIC = 0x00c0ffee
HPFS_SUPER_MAGIC = 0xF995E849
HUGETLBFS_MAGIC = 0x958458f6
ISOFS_SUPER_MAGIC = 0x9660
JFFS2_SUPER_MAGIC = 0x72b6
JFS_SUPER_MAGIC = 0x3153464a
MINIX_SUPER_MAGIC = 0x137F /* orig. minix */
MINIX_SUPER_MAGIC2 = 0x138F /* 30 char minix */
MINIX2_SUPER_MAGIC = 0x2468 /* minix V2 */
MINIX2_SUPER_MAGIC2 = 0x2478 /* minix V2, 30 char names */
MINIX3_SUPER_MAGIC = 0x4d5a /* minix V3 fs, 60 char names */
MQUEUE_MAGIC = 0x19800202
MSDOS_SUPER_MAGIC = 0x4d44
NCP_SUPER_MAGIC = 0x564c
NFS_SUPER_MAGIC = 0x6969
NILFS_SUPER_MAGIC = 0x3434
NTFS_SB_MAGIC = 0x5346544e
OCFS2_SUPER_MAGIC = 0x7461636f
OPENPROM_SUPER_MAGIC = 0x9fa1
PIPEFS_MAGIC = 0x50495045
PROC_SUPER_MAGIC = 0x9fa0
PSTOREFS_MAGIC = 0x6165676C
QNX4_SUPER_MAGIC = 0x002f
QNX6_SUPER_MAGIC = 0x68191122
RAMFS_MAGIC = 0x858458f6
REISERFS_SUPER_MAGIC = 0x52654973
ROMFS_MAGIC = 0x7275
SELINUX_MAGIC = 0xf97cff8c
SMACK_MAGIC = 0x43415d53
SMB_SUPER_MAGIC = 0x517B
SOCKFS_MAGIC = 0x534F434B
SQUASHFS_MAGIC = 0x73717368
SYSFS_MAGIC = 0x62656572
SYSV2_SUPER_MAGIC = 0x012FF7B6
SYSV4_SUPER_MAGIC = 0x012FF7B5
TMPFS_MAGIC = 0x01021994
UDF_SUPER_MAGIC = 0x15013346
UFS_MAGIC = 0x00011954
USBDEVICE_SUPER_MAGIC = 0x9fa2
V9FS_MAGIC = 0x01021997
VXFS_SUPER_MAGIC = 0xa501FCF5
XENFS_SUPER_MAGIC = 0xabba1974
XENIX_SUPER_MAGIC = 0x012FF7B4
XFS_SUPER_MAGIC = 0x58465342
_XIAFS_SUPER_MAGIC = 0x012FD16D
AFS_SUPER_MAGIC = 0x5346414F
AUFS_SUPER_MAGIC = 0x61756673
ANON_INODE_FS_SUPER_MAGIC = 0x09041934
BPF_FS_MAGIC = 0xCAFE4A11
CEPH_SUPER_MAGIC = 0x00C36400
CGROUP2_SUPER_MAGIC = 0x63677270
CONFIGFS_MAGIC = 0x62656570
ECRYPTFS_SUPER_MAGIC = 0xF15F
F2FS_SUPER_MAGIC = 0xF2F52010
FAT_SUPER_MAGIC = 0x4006
FHGFS_SUPER_MAGIC = 0x19830326
FUSEBLK_SUPER_MAGIC = 0x65735546
FUSECTL_SUPER_MAGIC = 0x65735543
GFS_SUPER_MAGIC = 0x1161970
GPFS_SUPER_MAGIC = 0x47504653
MTD_INODE_FS_SUPER_MAGIC = 0x11307854
INOTIFYFS_SUPER_MAGIC = 0x2BAD1DEA
ISOFS_R_WIN_SUPER_MAGIC = 0x4004
ISOFS_WIN_SUPER_MAGIC = 0x4000
JFFS_SUPER_MAGIC = 0x07C0
KAFS_SUPER_MAGIC = 0x6B414653
LUSTRE_SUPER_MAGIC = 0x0BD00BD0
NFSD_SUPER_MAGIC = 0x6E667364
NSFS_MAGIC = 0x6E736673
PANFS_SUPER_MAGIC = 0xAAD7AAEA
RPC_PIPEFS_SUPER_MAGIC = 0x67596969
SECURITYFS_SUPER_MAGIC = 0x73636673
TRACEFS_MAGIC = 0x74726163
UFS_BYTESWAPPED_SUPER_MAGIC = 0x54190100
VMHGFS_SUPER_MAGIC = 0xBACBACBC
VZFS_SUPER_MAGIC = 0x565A4653
ZFS_SUPER_MAGIC = 0x2FC12FC1
)
// coreutils/src/stat.c
var fsTypeMap = map[int64]string{
ADFS_SUPER_MAGIC: "adfs", /* 0xADF5 local */
AFFS_SUPER_MAGIC: "affs", /* 0xADFF local */
AFS_SUPER_MAGIC: "afs", /* 0x5346414F remote */
ANON_INODE_FS_SUPER_MAGIC: "anon-inode FS", /* 0x09041934 local */
AUFS_SUPER_MAGIC: "aufs", /* 0x61756673 remote */
// AUTOFS_SUPER_MAGIC: "autofs", /* 0x0187 local */
BEFS_SUPER_MAGIC: "befs", /* 0x42465331 local */
BDEVFS_MAGIC: "bdevfs", /* 0x62646576 local */
BFS_MAGIC: "bfs", /* 0x1BADFACE local */
BINFMTFS_MAGIC: "binfmt_misc", /* 0x42494E4D local */
BPF_FS_MAGIC: "bpf", /* 0xCAFE4A11 local */
BTRFS_SUPER_MAGIC: "btrfs", /* 0x9123683E local */
CEPH_SUPER_MAGIC: "ceph", /* 0x00C36400 remote */
CGROUP_SUPER_MAGIC: "cgroupfs", /* 0x0027E0EB local */
CGROUP2_SUPER_MAGIC: "cgroup2fs", /* 0x63677270 local */
CIFS_MAGIC_NUMBER: "cifs", /* 0xFF534D42 remote */
CODA_SUPER_MAGIC: "coda", /* 0x73757245 remote */
COH_SUPER_MAGIC: "coh", /* 0x012FF7B7 local */
CONFIGFS_MAGIC: "configfs", /* 0x62656570 local */
CRAMFS_MAGIC: "cramfs", /* 0x28CD3D45 local */
DEBUGFS_MAGIC: "debugfs", /* 0x64626720 local */
DEVFS_SUPER_MAGIC: "devfs", /* 0x1373 local */
DEVPTS_SUPER_MAGIC: "devpts", /* 0x1CD1 local */
ECRYPTFS_SUPER_MAGIC: "ecryptfs", /* 0xF15F local */
EFIVARFS_MAGIC: "efivarfs", /* 0xDE5E81E4 local */
EFS_SUPER_MAGIC: "efs", /* 0x00414A53 local */
EXT_SUPER_MAGIC: "ext", /* 0x137D local */
EXT2_SUPER_MAGIC: "ext2/ext3", /* 0xEF53 local */
EXT2_OLD_SUPER_MAGIC: "ext2", /* 0xEF51 local */
F2FS_SUPER_MAGIC: "f2fs", /* 0xF2F52010 local */
FAT_SUPER_MAGIC: "fat", /* 0x4006 local */
FHGFS_SUPER_MAGIC: "fhgfs", /* 0x19830326 remote */
FUSEBLK_SUPER_MAGIC: "fuseblk", /* 0x65735546 remote */
FUSECTL_SUPER_MAGIC: "fusectl", /* 0x65735543 remote */
FUTEXFS_SUPER_MAGIC: "futexfs", /* 0x0BAD1DEA local */
GFS_SUPER_MAGIC: "gfs/gfs2", /* 0x1161970 remote */
GPFS_SUPER_MAGIC: "gpfs", /* 0x47504653 remote */
HFS_SUPER_MAGIC: "hfs", /* 0x4244 local */
HFSPLUS_SUPER_MAGIC: "hfsplus", /* 0x482b local */
HPFS_SUPER_MAGIC: "hpfs", /* 0xF995E849 local */
HUGETLBFS_MAGIC: "hugetlbfs", /* 0x958458F6 local */
MTD_INODE_FS_SUPER_MAGIC: "inodefs", /* 0x11307854 local */
INOTIFYFS_SUPER_MAGIC: "inotifyfs", /* 0x2BAD1DEA local */
ISOFS_SUPER_MAGIC: "isofs", /* 0x9660 local */
ISOFS_R_WIN_SUPER_MAGIC: "isofs", /* 0x4004 local */
ISOFS_WIN_SUPER_MAGIC: "isofs", /* 0x4000 local */
JFFS_SUPER_MAGIC: "jffs", /* 0x07C0 local */
JFFS2_SUPER_MAGIC: "jffs2", /* 0x72B6 local */
JFS_SUPER_MAGIC: "jfs", /* 0x3153464A local */
KAFS_SUPER_MAGIC: "k-afs", /* 0x6B414653 remote */
LUSTRE_SUPER_MAGIC: "lustre", /* 0x0BD00BD0 remote */
MINIX_SUPER_MAGIC: "minix", /* 0x137F local */
MINIX_SUPER_MAGIC2: "minix (30 char.)", /* 0x138F local */
MINIX2_SUPER_MAGIC: "minix v2", /* 0x2468 local */
MINIX2_SUPER_MAGIC2: "minix v2 (30 char.)", /* 0x2478 local */
MINIX3_SUPER_MAGIC: "minix3", /* 0x4D5A local */
MQUEUE_MAGIC: "mqueue", /* 0x19800202 local */
MSDOS_SUPER_MAGIC: "msdos", /* 0x4D44 local */
NCP_SUPER_MAGIC: "novell", /* 0x564C remote */
NFS_SUPER_MAGIC: "nfs", /* 0x6969 remote */
NFSD_SUPER_MAGIC: "nfsd", /* 0x6E667364 remote */
NILFS_SUPER_MAGIC: "nilfs", /* 0x3434 local */
NSFS_MAGIC: "nsfs", /* 0x6E736673 local */
NTFS_SB_MAGIC: "ntfs", /* 0x5346544E local */
OPENPROM_SUPER_MAGIC: "openprom", /* 0x9FA1 local */
OCFS2_SUPER_MAGIC: "ocfs2", /* 0x7461636f remote */
PANFS_SUPER_MAGIC: "panfs", /* 0xAAD7AAEA remote */
PIPEFS_MAGIC: "pipefs", /* 0x50495045 remote */
PROC_SUPER_MAGIC: "proc", /* 0x9FA0 local */
PSTOREFS_MAGIC: "pstorefs", /* 0x6165676C local */
QNX4_SUPER_MAGIC: "qnx4", /* 0x002F local */
QNX6_SUPER_MAGIC: "qnx6", /* 0x68191122 local */
RAMFS_MAGIC: "ramfs", /* 0x858458F6 local */
REISERFS_SUPER_MAGIC: "reiserfs", /* 0x52654973 local */
ROMFS_MAGIC: "romfs", /* 0x7275 local */
RPC_PIPEFS_SUPER_MAGIC: "rpc_pipefs", /* 0x67596969 local */
SECURITYFS_SUPER_MAGIC: "securityfs", /* 0x73636673 local */
SELINUX_MAGIC: "selinux", /* 0xF97CFF8C local */
SMB_SUPER_MAGIC: "smb", /* 0x517B remote */
SOCKFS_MAGIC: "sockfs", /* 0x534F434B local */
SQUASHFS_MAGIC: "squashfs", /* 0x73717368 local */
SYSFS_MAGIC: "sysfs", /* 0x62656572 local */
SYSV2_SUPER_MAGIC: "sysv2", /* 0x012FF7B6 local */
SYSV4_SUPER_MAGIC: "sysv4", /* 0x012FF7B5 local */
TMPFS_MAGIC: "tmpfs", /* 0x01021994 local */
TRACEFS_MAGIC: "tracefs", /* 0x74726163 local */
UDF_SUPER_MAGIC: "udf", /* 0x15013346 local */
UFS_MAGIC: "ufs", /* 0x00011954 local */
UFS_BYTESWAPPED_SUPER_MAGIC: "ufs", /* 0x54190100 local */
USBDEVICE_SUPER_MAGIC: "usbdevfs", /* 0x9FA2 local */
V9FS_MAGIC: "v9fs", /* 0x01021997 local */
VMHGFS_SUPER_MAGIC: "vmhgfs", /* 0xBACBACBC remote */
VXFS_SUPER_MAGIC: "vxfs", /* 0xA501FCF5 local */
VZFS_SUPER_MAGIC: "vzfs", /* 0x565A4653 local */
XENFS_SUPER_MAGIC: "xenfs", /* 0xABBA1974 local */
XENIX_SUPER_MAGIC: "xenix", /* 0x012FF7B4 local */
XFS_SUPER_MAGIC: "xfs", /* 0x58465342 local */
_XIAFS_SUPER_MAGIC: "xia", /* 0x012FD16D local */
ZFS_SUPER_MAGIC: "zfs", /* 0x2FC12FC1 local */
}
// readMountFile reads mountinfo or mounts file under the specified root path
// (eg, /proc/1, /proc/self, etc)
func readMountFile(root string) (lines []string, useMounts bool, filename string, err error) {
filename = path.Join(root, "mountinfo")
lines, err = common.ReadLines(filename)
if err != nil {
var pathErr *os.PathError
if !errors.As(err, &pathErr) {
return
}
// if kernel does not support 1/mountinfo, fallback to 1/mounts (<2.6.26)
useMounts = true
filename = path.Join(root, "mounts")
lines, err = common.ReadLines(filename)
if err != nil {
return
}
return
}
return
}
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
// by default, try "/proc/1/..." first
root := common.HostProc(path.Join("1"))
// force preference for dirname of HOST_PROC_MOUNTINFO, if set #1271
hpmPath := os.Getenv("HOST_PROC_MOUNTINFO")
if hpmPath != "" {
root = filepath.Dir(hpmPath)
}
lines, useMounts, filename, err := readMountFile(root)
if err != nil {
if hpmPath != "" { // don't fallback with HOST_PROC_MOUNTINFO
return nil, err
}
// fallback to "/proc/self/..." #1159
lines, useMounts, filename, err = readMountFile(common.HostProc(path.Join("self")))
if err != nil {
return nil, err
}
}
fs, err := getFileSystems()
if err != nil && !all {
return nil, err
}
ret := make([]PartitionStat, 0, len(lines))
for _, line := range lines {
var d PartitionStat
if useMounts {
fields := strings.Fields(line)
d = PartitionStat{
Device: fields[0],
Mountpoint: unescapeFstab(fields[1]),
Fstype: fields[2],
Opts: strings.Fields(fields[3]),
}
if !all {
if d.Device == "none" || !common.StringsHas(fs, d.Fstype) {
continue
}
}
} else {
// a line of 1/mountinfo has the following structure:
// 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
// (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11)
// split the mountinfo line by the separator hyphen
parts := strings.Split(line, " - ")
if len(parts) != 2 {
return nil, fmt.Errorf("found invalid mountinfo line in file %s: %s ", filename, line)
}
fields := strings.Fields(parts[0])
blockDeviceID := fields[2]
mountPoint := fields[4]
mountOpts := strings.Split(fields[5], ",")
if rootDir := fields[3]; rootDir != "" && rootDir != "/" {
mountOpts = append(mountOpts, "bind")
}
fields = strings.Fields(parts[1])
fstype := fields[0]
device := fields[1]
d = PartitionStat{
Device: device,
Mountpoint: unescapeFstab(mountPoint),
Fstype: fstype,
Opts: mountOpts,
}
if !all {
if d.Device == "none" || !common.StringsHas(fs, d.Fstype) {
continue
}
}
if strings.HasPrefix(d.Device, "/dev/mapper/") {
devpath, err := filepath.EvalSymlinks(common.HostDev(strings.Replace(d.Device, "/dev", "", -1)))
if err == nil {
d.Device = devpath
}
}
// /dev/root is not the real device name
// so we get the real device name from its major/minor number
if d.Device == "/dev/root" {
devpath, err := os.Readlink(common.HostSys("/dev/block/" + blockDeviceID))
if err == nil {
d.Device = strings.Replace(d.Device, "root", filepath.Base(devpath), 1)
}
}
}
ret = append(ret, d)
}
return ret, nil
}
// getFileSystems returns supported filesystems from /proc/filesystems
func getFileSystems() ([]string, error) {
filename := common.HostProc("filesystems")
lines, err := common.ReadLines(filename)
if err != nil {
return nil, err
}
var ret []string
for _, line := range lines {
if !strings.HasPrefix(line, "nodev") {
ret = append(ret, strings.TrimSpace(line))
continue
}
t := strings.Split(line, "\t")
if len(t) != 2 || t[1] != "zfs" {
continue
}
ret = append(ret, strings.TrimSpace(t[1]))
}
return ret, nil
}
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
filename := common.HostProc("diskstats")
lines, err := common.ReadLines(filename)
if err != nil {
return nil, err
}
ret := make(map[string]IOCountersStat)
empty := IOCountersStat{}
// use only basename such as "/dev/sda1" to "sda1"
for i, name := range names {
names[i] = filepath.Base(name)
}
for _, line := range lines {
fields := strings.Fields(line)
if len(fields) < 14 {
// malformed line in /proc/diskstats, avoid panic by ignoring.
continue
}
name := fields[2]
if len(names) > 0 && !common.StringsHas(names, name) {
continue
}
reads, err := strconv.ParseUint((fields[3]), 10, 64)
if err != nil {
return ret, err
}
mergedReads, err := strconv.ParseUint((fields[4]), 10, 64)
if err != nil {
return ret, err
}
rbytes, err := strconv.ParseUint((fields[5]), 10, 64)
if err != nil {
return ret, err
}
rtime, err := strconv.ParseUint((fields[6]), 10, 64)
if err != nil {
return ret, err
}
writes, err := strconv.ParseUint((fields[7]), 10, 64)
if err != nil {
return ret, err
}
mergedWrites, err := strconv.ParseUint((fields[8]), 10, 64)
if err != nil {
return ret, err
}
wbytes, err := strconv.ParseUint((fields[9]), 10, 64)
if err != nil {
return ret, err
}
wtime, err := strconv.ParseUint((fields[10]), 10, 64)
if err != nil {
return ret, err
}
iopsInProgress, err := strconv.ParseUint((fields[11]), 10, 64)
if err != nil {
return ret, err
}
iotime, err := strconv.ParseUint((fields[12]), 10, 64)
if err != nil {
return ret, err
}
weightedIO, err := strconv.ParseUint((fields[13]), 10, 64)
if err != nil {
return ret, err
}
d := IOCountersStat{
ReadBytes: rbytes * sectorSize,
WriteBytes: wbytes * sectorSize,
ReadCount: reads,
WriteCount: writes,
MergedReadCount: mergedReads,
MergedWriteCount: mergedWrites,
ReadTime: rtime,
WriteTime: wtime,
IopsInProgress: iopsInProgress,
IoTime: iotime,
WeightedIO: weightedIO,
}
if d == empty {
continue
}
d.Name = name
d.SerialNumber, _ = SerialNumberWithContext(ctx, name)
d.Label, _ = LabelWithContext(ctx, name)
ret[name] = d
}
return ret, nil
}
func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
var stat unix.Stat_t
err := unix.Stat(name, &stat)
if err != nil {
return "", err
}
major := unix.Major(uint64(stat.Rdev))
minor := unix.Minor(uint64(stat.Rdev))
// Try to get the serial from udev data
udevDataPath := common.HostRun(fmt.Sprintf("udev/data/b%d:%d", major, minor))
if udevdata, err := ioutil.ReadFile(udevDataPath); err == nil {
scanner := bufio.NewScanner(bytes.NewReader(udevdata))
for scanner.Scan() {
values := strings.Split(scanner.Text(), "=")
if len(values) == 2 && values[0] == "E:ID_SERIAL" {
return values[1], nil
}
}
}
// Try to get the serial from sysfs, look at the disk device (minor 0) directly
// because if it is a partition it is not going to contain any device information
devicePath := common.HostSys(fmt.Sprintf("dev/block/%d:0/device", major))
model, _ := ioutil.ReadFile(filepath.Join(devicePath, "model"))
serial, _ := ioutil.ReadFile(filepath.Join(devicePath, "serial"))
if len(model) > 0 && len(serial) > 0 {
return fmt.Sprintf("%s_%s", string(model), string(serial)), nil
}
return "", nil
}
func LabelWithContext(ctx context.Context, name string) (string, error) {
// Try label based on devicemapper name
dmname_filename := common.HostSys(fmt.Sprintf("block/%s/dm/name", name))
if !common.PathExists(dmname_filename) {
return "", nil
}
dmname, err := ioutil.ReadFile(dmname_filename)
if err != nil {
return "", err
}
return strings.TrimSpace(string(dmname)), nil
}
func getFsType(stat unix.Statfs_t) string {
t := int64(stat.Type)
ret, ok := fsTypeMap[t]
if !ok {
return ""
}
return ret
}
+159
View File
@@ -0,0 +1,159 @@
//go:build openbsd
// +build openbsd
package disk
import (
"bytes"
"context"
"encoding/binary"
"github.com/shirou/gopsutil/v3/internal/common"
"golang.org/x/sys/unix"
)
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
var ret []PartitionStat
// get length
count, err := unix.Getfsstat(nil, unix.MNT_WAIT)
if err != nil {
return ret, err
}
fs := make([]unix.Statfs_t, count)
if _, err = unix.Getfsstat(fs, unix.MNT_WAIT); err != nil {
return ret, err
}
for _, stat := range fs {
opts := []string{"rw"}
if stat.F_flags&unix.MNT_RDONLY != 0 {
opts = []string{"rw"}
}
if stat.F_flags&unix.MNT_SYNCHRONOUS != 0 {
opts = append(opts, "sync")
}
if stat.F_flags&unix.MNT_NOEXEC != 0 {
opts = append(opts, "noexec")
}
if stat.F_flags&unix.MNT_NOSUID != 0 {
opts = append(opts, "nosuid")
}
if stat.F_flags&unix.MNT_NODEV != 0 {
opts = append(opts, "nodev")
}
if stat.F_flags&unix.MNT_ASYNC != 0 {
opts = append(opts, "async")
}
if stat.F_flags&unix.MNT_SOFTDEP != 0 {
opts = append(opts, "softdep")
}
if stat.F_flags&unix.MNT_NOATIME != 0 {
opts = append(opts, "noatime")
}
if stat.F_flags&unix.MNT_WXALLOWED != 0 {
opts = append(opts, "wxallowed")
}
d := PartitionStat{
Device: common.ByteToString(stat.F_mntfromname[:]),
Mountpoint: common.ByteToString(stat.F_mntonname[:]),
Fstype: common.ByteToString(stat.F_fstypename[:]),
Opts: opts,
}
ret = append(ret, d)
}
return ret, nil
}
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
ret := make(map[string]IOCountersStat)
r, err := unix.SysctlRaw("hw.diskstats")
if err != nil {
return nil, err
}
buf := []byte(r)
length := len(buf)
count := int(uint64(length) / uint64(sizeOfDiskstats))
// parse buf to Diskstats
for i := 0; i < count; i++ {
b := buf[i*sizeOfDiskstats : i*sizeOfDiskstats+sizeOfDiskstats]
d, err := parseDiskstats(b)
if err != nil {
continue
}
name := common.IntToString(d.Name[:])
if len(names) > 0 && !common.StringsHas(names, name) {
continue
}
ds := IOCountersStat{
ReadCount: d.Rxfer,
WriteCount: d.Wxfer,
ReadBytes: d.Rbytes,
WriteBytes: d.Wbytes,
Name: name,
}
ret[name] = ds
}
return ret, nil
}
// BT2LD(time) ((long double)(time).sec + (time).frac * BINTIME_SCALE)
func parseDiskstats(buf []byte) (Diskstats, error) {
var ds Diskstats
br := bytes.NewReader(buf)
// err := binary.Read(br, binary.LittleEndian, &ds)
err := common.Read(br, binary.LittleEndian, &ds)
if err != nil {
return ds, err
}
return ds, nil
}
func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
stat := unix.Statfs_t{}
err := unix.Statfs(path, &stat)
if err != nil {
return nil, err
}
bsize := stat.F_bsize
ret := &UsageStat{
Path: path,
Fstype: getFsType(stat),
Total: (uint64(stat.F_blocks) * uint64(bsize)),
Free: (uint64(stat.F_bavail) * uint64(bsize)),
InodesTotal: (uint64(stat.F_files)),
InodesFree: (uint64(stat.F_ffree)),
}
ret.InodesUsed = (ret.InodesTotal - ret.InodesFree)
ret.InodesUsedPercent = (float64(ret.InodesUsed) / float64(ret.InodesTotal)) * 100.0
ret.Used = (uint64(stat.F_blocks) - uint64(stat.F_bfree)) * uint64(bsize)
ret.UsedPercent = (float64(ret.Used) / float64(ret.Total)) * 100.0
return ret, nil
}
func getFsType(stat unix.Statfs_t) string {
return common.ByteToString(stat.F_fstypename[:])
}
func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
return "", common.ErrNotImplementedError
}
func LabelWithContext(ctx context.Context, name string) (string, error) {
return "", common.ErrNotImplementedError
}
+38
View File
@@ -0,0 +1,38 @@
//go:build openbsd && 386
// +build openbsd,386
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
// cgo -godefs disk/types_openbsd.go
package disk
const (
devstat_NO_DATA = 0x00
devstat_READ = 0x01
devstat_WRITE = 0x02
devstat_FREE = 0x03
)
const (
sizeOfDiskstats = 0x60
)
type Diskstats struct {
Name [16]int8
Busy int32
Rxfer uint64
Wxfer uint64
Seek uint64
Rbytes uint64
Wbytes uint64
Attachtime Timeval
Timestamp Timeval
Time Timeval
}
type Timeval struct {
Sec int64
Usec int32
}
type Diskstat struct{}
type bintime struct{}
+36
View File
@@ -0,0 +1,36 @@
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
// cgo -godefs types_openbsd.go
package disk
const (
devstat_NO_DATA = 0x00
devstat_READ = 0x01
devstat_WRITE = 0x02
devstat_FREE = 0x03
)
const (
sizeOfDiskstats = 0x70
)
type Diskstats struct {
Name [16]int8
Busy int32
Pad_cgo_0 [4]byte
Rxfer uint64
Wxfer uint64
Seek uint64
Rbytes uint64
Wbytes uint64
Attachtime Timeval
Timestamp Timeval
Time Timeval
}
type Timeval struct {
Sec int64
Usec int64
}
type Diskstat struct{}
type bintime struct{}
+38
View File
@@ -0,0 +1,38 @@
//go:build openbsd && arm
// +build openbsd,arm
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
// cgo -godefs disk/types_openbsd.go
package disk
const (
devstat_NO_DATA = 0x00
devstat_READ = 0x01
devstat_WRITE = 0x02
devstat_FREE = 0x03
)
const (
sizeOfDiskstats = 0x60
)
type Diskstats struct {
Name [16]int8
Busy int32
Rxfer uint64
Wxfer uint64
Seek uint64
Rbytes uint64
Wbytes uint64
Attachtime Timeval
Timestamp Timeval
Time Timeval
}
type Timeval struct {
Sec int64
Usec int32
}
type Diskstat struct{}
type bintime struct{}
+38
View File
@@ -0,0 +1,38 @@
//go:build openbsd && arm64
// +build openbsd,arm64
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
// cgo -godefs disk/types_openbsd.go
package disk
const (
devstat_NO_DATA = 0x00
devstat_READ = 0x01
devstat_WRITE = 0x02
devstat_FREE = 0x03
)
const (
sizeOfDiskstats = 0x70
)
type Diskstats struct {
Name [16]int8
Busy int32
Rxfer uint64
Wxfer uint64
Seek uint64
Rbytes uint64
Wbytes uint64
Attachtime Timeval
Timestamp Timeval
Time Timeval
}
type Timeval struct {
Sec int64
Usec int64
}
type Diskstat struct{}
type bintime struct{}
+147
View File
@@ -0,0 +1,147 @@
//go:build solaris
// +build solaris
package disk
import (
"bufio"
"bytes"
"context"
"fmt"
"math"
"os"
"strings"
"github.com/shirou/gopsutil/v3/internal/common"
"golang.org/x/sys/unix"
)
const (
// _DEFAULT_NUM_MOUNTS is set to `cat /etc/mnttab | wc -l` rounded up to the
// nearest power of two.
_DEFAULT_NUM_MOUNTS = 32
// _MNTTAB default place to read mount information
_MNTTAB = "/etc/mnttab"
)
// A blacklist of read-only virtual filesystems. Writable filesystems are of
// operational concern and must not be included in this list.
var fsTypeBlacklist = map[string]struct{}{
"ctfs": {},
"dev": {},
"fd": {},
"lofs": {},
"lxproc": {},
"mntfs": {},
"objfs": {},
"proc": {},
}
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
ret := make([]PartitionStat, 0, _DEFAULT_NUM_MOUNTS)
// Scan mnttab(4)
f, err := os.Open(_MNTTAB)
if err != nil {
}
defer func() {
if err == nil {
err = f.Close()
} else {
f.Close()
}
}()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
fields := strings.Split(scanner.Text(), "\t")
if _, found := fsTypeBlacklist[fields[2]]; found {
continue
}
ret = append(ret, PartitionStat{
// NOTE(seanc@): Device isn't exactly accurate: from mnttab(4): "The name
// of the resource that has been mounted." Ideally this value would come
// from Statvfs_t.Fsid but I'm leaving it to the caller to traverse
// unix.Statvfs().
Device: fields[0],
Mountpoint: fields[1],
Fstype: fields[2],
Opts: strings.Split(fields[3], ","),
})
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("unable to scan %q: %v", _MNTTAB, err)
}
return ret, err
}
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
return nil, common.ErrNotImplementedError
}
func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
statvfs := unix.Statvfs_t{}
if err := unix.Statvfs(path, &statvfs); err != nil {
return nil, fmt.Errorf("unable to call statvfs(2) on %q: %v", path, err)
}
usageStat := &UsageStat{
Path: path,
Fstype: common.IntToString(statvfs.Basetype[:]),
Total: statvfs.Blocks * statvfs.Frsize,
Free: statvfs.Bfree * statvfs.Frsize,
Used: (statvfs.Blocks - statvfs.Bfree) * statvfs.Frsize,
// NOTE: ZFS (and FreeBZSD's UFS2) use dynamic inode/dnode allocation.
// Explicitly return a near-zero value for InodesUsedPercent so that nothing
// attempts to garbage collect based on a lack of available inodes/dnodes.
// Similarly, don't use the zero value to prevent divide-by-zero situations
// and inject a faux near-zero value. Filesystems evolve. Has your
// filesystem evolved? Probably not if you care about the number of
// available inodes.
InodesTotal: 1024.0 * 1024.0,
InodesUsed: 1024.0,
InodesFree: math.MaxUint64,
InodesUsedPercent: (1024.0 / (1024.0 * 1024.0)) * 100.0,
}
usageStat.UsedPercent = (float64(usageStat.Used) / float64(usageStat.Total)) * 100.0
return usageStat, nil
}
func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
out, err := invoke.CommandWithContext(ctx, "cfgadm", "-ls", "select=type(disk),cols=ap_id:info,cols2=,noheadings")
if err != nil {
return "", fmt.Errorf("exec cfgadm: %w", err)
}
suf := "::" + strings.TrimPrefix(name, "/dev/")
s := bufio.NewScanner(bytes.NewReader(out))
for s.Scan() {
flds := strings.Fields(s.Text())
if strings.HasSuffix(flds[0], suf) {
flen := len(flds)
if flen >= 3 {
for i, f := range flds {
if i > 0 && i < flen-1 && f == "SN:" {
return flds[i+1], nil
}
}
}
return "", nil
}
}
if err := s.Err(); err != nil {
return "", err
}
return "", nil
}
func LabelWithContext(ctx context.Context, name string) (string, error) {
return "", common.ErrNotImplementedError
}
+63
View File
@@ -0,0 +1,63 @@
//go:build freebsd || linux || darwin || (aix && !cgo)
// +build freebsd linux darwin aix,!cgo
package disk
import (
"context"
"strconv"
"golang.org/x/sys/unix"
)
func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
stat := unix.Statfs_t{}
err := unix.Statfs(path, &stat)
if err != nil {
return nil, err
}
bsize := stat.Bsize
ret := &UsageStat{
Path: unescapeFstab(path),
Fstype: getFsType(stat),
Total: (uint64(stat.Blocks) * uint64(bsize)),
Free: (uint64(stat.Bavail) * uint64(bsize)),
InodesTotal: (uint64(stat.Files)),
InodesFree: (uint64(stat.Ffree)),
}
ret.Used = (uint64(stat.Blocks) - uint64(stat.Bfree)) * uint64(bsize)
if (ret.Used + ret.Free) == 0 {
ret.UsedPercent = 0
} else {
// We don't use ret.Total to calculate percent.
// see https://github.com/shirou/gopsutil/issues/562
ret.UsedPercent = (float64(ret.Used) / float64(ret.Used+ret.Free)) * 100.0
}
// if could not get InodesTotal, return empty
if ret.InodesTotal < ret.InodesFree {
return ret, nil
}
ret.InodesUsed = (ret.InodesTotal - ret.InodesFree)
if ret.InodesTotal == 0 {
ret.InodesUsedPercent = 0
} else {
ret.InodesUsedPercent = (float64(ret.InodesUsed) / float64(ret.InodesTotal)) * 100.0
}
return ret, nil
}
// Unescape escaped octal chars (like space 040, ampersand 046 and backslash 134) to their real value in fstab fields issue#555
func unescapeFstab(path string) string {
escaped, err := strconv.Unquote(`"` + path + `"`)
if err != nil {
return path
}
return escaped
}
+207
View File
@@ -0,0 +1,207 @@
//go:build windows
// +build windows
package disk
import (
"bytes"
"context"
"fmt"
"syscall"
"unsafe"
"github.com/shirou/gopsutil/v3/internal/common"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
)
var (
procGetDiskFreeSpaceExW = common.Modkernel32.NewProc("GetDiskFreeSpaceExW")
procGetLogicalDriveStringsW = common.Modkernel32.NewProc("GetLogicalDriveStringsW")
procGetDriveType = common.Modkernel32.NewProc("GetDriveTypeW")
procGetVolumeInformation = common.Modkernel32.NewProc("GetVolumeInformationW")
)
var (
fileFileCompression = int64(16) // 0x00000010
fileReadOnlyVolume = int64(524288) // 0x00080000
)
// diskPerformance is an equivalent representation of DISK_PERFORMANCE in the Windows API.
// https://docs.microsoft.com/fr-fr/windows/win32/api/winioctl/ns-winioctl-disk_performance
type diskPerformance struct {
BytesRead int64
BytesWritten int64
ReadTime int64
WriteTime int64
IdleTime int64
ReadCount uint32
WriteCount uint32
QueueDepth uint32
SplitCount uint32
QueryTime int64
StorageDeviceNumber uint32
StorageManagerName [8]uint16
alignmentPadding uint32 // necessary for 32bit support, see https://github.com/elastic/beats/pull/16553
}
func init() {
// enable disk performance counters on Windows Server editions (needs to run as admin)
key, err := registry.OpenKey(registry.LOCAL_MACHINE, `SYSTEM\CurrentControlSet\Services\PartMgr`, registry.SET_VALUE)
if err == nil {
key.SetDWordValue("EnableCounterForIoctl", 1)
}
}
func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
lpFreeBytesAvailable := int64(0)
lpTotalNumberOfBytes := int64(0)
lpTotalNumberOfFreeBytes := int64(0)
diskret, _, err := procGetDiskFreeSpaceExW.Call(
uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(path))),
uintptr(unsafe.Pointer(&lpFreeBytesAvailable)),
uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)),
uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes)))
if diskret == 0 {
return nil, err
}
ret := &UsageStat{
Path: path,
Total: uint64(lpTotalNumberOfBytes),
Free: uint64(lpTotalNumberOfFreeBytes),
Used: uint64(lpTotalNumberOfBytes) - uint64(lpTotalNumberOfFreeBytes),
UsedPercent: (float64(lpTotalNumberOfBytes) - float64(lpTotalNumberOfFreeBytes)) / float64(lpTotalNumberOfBytes) * 100,
// InodesTotal: 0,
// InodesFree: 0,
// InodesUsed: 0,
// InodesUsedPercent: 0,
}
return ret, nil
}
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
warnings := common.Warnings{
Verbose: true,
}
var ret []PartitionStat
lpBuffer := make([]byte, 254)
diskret, _, err := procGetLogicalDriveStringsW.Call(
uintptr(len(lpBuffer)),
uintptr(unsafe.Pointer(&lpBuffer[0])))
if diskret == 0 {
return ret, err
}
for _, v := range lpBuffer {
if v >= 65 && v <= 90 {
path := string(v) + ":"
typepath, _ := windows.UTF16PtrFromString(path)
typeret, _, _ := procGetDriveType.Call(uintptr(unsafe.Pointer(typepath)))
if typeret == 0 {
err := windows.GetLastError()
warnings.Add(err)
continue
}
// 2: DRIVE_REMOVABLE 3: DRIVE_FIXED 4: DRIVE_REMOTE 5: DRIVE_CDROM
if typeret == 2 || typeret == 3 || typeret == 4 || typeret == 5 {
lpVolumeNameBuffer := make([]byte, 256)
lpVolumeSerialNumber := int64(0)
lpMaximumComponentLength := int64(0)
lpFileSystemFlags := int64(0)
lpFileSystemNameBuffer := make([]byte, 256)
volpath, _ := windows.UTF16PtrFromString(string(v) + ":/")
driveret, _, err := procGetVolumeInformation.Call(
uintptr(unsafe.Pointer(volpath)),
uintptr(unsafe.Pointer(&lpVolumeNameBuffer[0])),
uintptr(len(lpVolumeNameBuffer)),
uintptr(unsafe.Pointer(&lpVolumeSerialNumber)),
uintptr(unsafe.Pointer(&lpMaximumComponentLength)),
uintptr(unsafe.Pointer(&lpFileSystemFlags)),
uintptr(unsafe.Pointer(&lpFileSystemNameBuffer[0])),
uintptr(len(lpFileSystemNameBuffer)))
if driveret == 0 {
if typeret == 5 || typeret == 2 {
continue // device is not ready will happen if there is no disk in the drive
}
warnings.Add(err)
continue
}
opts := []string{"rw"}
if lpFileSystemFlags&fileReadOnlyVolume != 0 {
opts = []string{"ro"}
}
if lpFileSystemFlags&fileFileCompression != 0 {
opts = append(opts, "compress")
}
d := PartitionStat{
Mountpoint: path,
Device: path,
Fstype: string(bytes.Replace(lpFileSystemNameBuffer, []byte("\x00"), []byte(""), -1)),
Opts: opts,
}
ret = append(ret, d)
}
}
}
return ret, warnings.Reference()
}
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
// https://github.com/giampaolo/psutil/blob/544e9daa4f66a9f80d7bf6c7886d693ee42f0a13/psutil/arch/windows/disk.c#L83
drivemap := make(map[string]IOCountersStat, 0)
var diskPerformance diskPerformance
lpBuffer := make([]uint16, 254)
lpBufferLen, err := windows.GetLogicalDriveStrings(uint32(len(lpBuffer)), &lpBuffer[0])
if err != nil {
return drivemap, err
}
for _, v := range lpBuffer[:lpBufferLen] {
if 'A' <= v && v <= 'Z' {
path := string(rune(v)) + ":"
typepath, _ := windows.UTF16PtrFromString(path)
typeret := windows.GetDriveType(typepath)
if typeret == 0 {
return drivemap, windows.GetLastError()
}
if typeret != windows.DRIVE_FIXED {
continue
}
szDevice := fmt.Sprintf(`\\.\%s`, path)
const IOCTL_DISK_PERFORMANCE = 0x70020
h, err := windows.CreateFile(syscall.StringToUTF16Ptr(szDevice), 0, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE, nil, windows.OPEN_EXISTING, 0, 0)
if err != nil {
if err == windows.ERROR_FILE_NOT_FOUND {
continue
}
return drivemap, err
}
defer windows.CloseHandle(h)
var diskPerformanceSize uint32
err = windows.DeviceIoControl(h, IOCTL_DISK_PERFORMANCE, nil, 0, (*byte)(unsafe.Pointer(&diskPerformance)), uint32(unsafe.Sizeof(diskPerformance)), &diskPerformanceSize, nil)
if err != nil {
return drivemap, err
}
drivemap[path] = IOCountersStat{
ReadBytes: uint64(diskPerformance.BytesRead),
WriteBytes: uint64(diskPerformance.BytesWritten),
ReadCount: uint64(diskPerformance.ReadCount),
WriteCount: uint64(diskPerformance.WriteCount),
ReadTime: uint64(diskPerformance.ReadTime / 10000 / 1000), // convert to ms: https://github.com/giampaolo/psutil/issues/1012
WriteTime: uint64(diskPerformance.WriteTime / 10000 / 1000),
Name: path,
}
}
}
return drivemap, nil
}
func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
return "", common.ErrNotImplementedError
}
func LabelWithContext(ctx context.Context, name string) (string, error) {
return "", common.ErrNotImplementedError
}
+129
View File
@@ -0,0 +1,129 @@
// https://github.com/lufia/iostat/blob/9f7362b77ad333b26c01c99de52a11bdb650ded2/iostat_darwin.c
#include <stdint.h>
#include <CoreFoundation/CoreFoundation.h>
#include "iostat_darwin.h"
#define IOKIT 1 /* to get io_name_t in device_types.h */
#include <IOKit/IOKitLib.h>
#include <IOKit/storage/IOBlockStorageDriver.h>
#include <IOKit/storage/IOMedia.h>
#include <IOKit/IOBSD.h>
#include <mach/mach_host.h>
static int getdrivestat(io_registry_entry_t d, DriveStats *stat);
static int fillstat(io_registry_entry_t d, DriveStats *stat);
int
gopsutil_v3_readdrivestat(DriveStats a[], int n)
{
CFMutableDictionaryRef match;
io_iterator_t drives;
io_registry_entry_t d;
kern_return_t status;
int na, rv;
match = IOServiceMatching("IOMedia");
CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
status = IOServiceGetMatchingServices(0, match, &drives);
if(status != KERN_SUCCESS)
return -1;
na = 0;
while(na < n && (d=IOIteratorNext(drives)) > 0){
rv = getdrivestat(d, &a[na]);
if(rv < 0)
return -1;
if(rv > 0)
na++;
IOObjectRelease(d);
}
IOObjectRelease(drives);
return na;
}
static int
getdrivestat(io_registry_entry_t d, DriveStats *stat)
{
io_registry_entry_t parent;
kern_return_t status;
CFDictionaryRef props;
CFStringRef name;
CFNumberRef num;
int rv;
memset(stat, 0, sizeof *stat);
status = IORegistryEntryGetParentEntry(d, kIOServicePlane, &parent);
if(status != KERN_SUCCESS)
return -1;
if(!IOObjectConformsTo(parent, "IOBlockStorageDriver")){
IOObjectRelease(parent);
return 0;
}
status = IORegistryEntryCreateCFProperties(d, (CFMutableDictionaryRef *)&props, kCFAllocatorDefault, kNilOptions);
if(status != KERN_SUCCESS){
IOObjectRelease(parent);
return -1;
}
name = (CFStringRef)CFDictionaryGetValue(props, CFSTR(kIOBSDNameKey));
CFStringGetCString(name, stat->name, NAMELEN, CFStringGetSystemEncoding());
num = (CFNumberRef)CFDictionaryGetValue(props, CFSTR(kIOMediaSizeKey));
CFNumberGetValue(num, kCFNumberSInt64Type, &stat->size);
num = (CFNumberRef)CFDictionaryGetValue(props, CFSTR(kIOMediaPreferredBlockSizeKey));
CFNumberGetValue(num, kCFNumberSInt64Type, &stat->blocksize);
CFRelease(props);
rv = fillstat(parent, stat);
IOObjectRelease(parent);
if(rv < 0)
return -1;
return 1;
}
static struct {
char *key;
size_t off;
} statstab[] = {
{kIOBlockStorageDriverStatisticsBytesReadKey, offsetof(DriveStats, read)},
{kIOBlockStorageDriverStatisticsBytesWrittenKey, offsetof(DriveStats, written)},
{kIOBlockStorageDriverStatisticsReadsKey, offsetof(DriveStats, nread)},
{kIOBlockStorageDriverStatisticsWritesKey, offsetof(DriveStats, nwrite)},
{kIOBlockStorageDriverStatisticsTotalReadTimeKey, offsetof(DriveStats, readtime)},
{kIOBlockStorageDriverStatisticsTotalWriteTimeKey, offsetof(DriveStats, writetime)},
{kIOBlockStorageDriverStatisticsLatentReadTimeKey, offsetof(DriveStats, readlat)},
{kIOBlockStorageDriverStatisticsLatentWriteTimeKey, offsetof(DriveStats, writelat)},
};
static int
fillstat(io_registry_entry_t d, DriveStats *stat)
{
CFDictionaryRef props, v;
CFNumberRef num;
kern_return_t status;
typeof(statstab[0]) *bp, *ep;
status = IORegistryEntryCreateCFProperties(d, (CFMutableDictionaryRef *)&props, kCFAllocatorDefault, kNilOptions);
if(status != KERN_SUCCESS)
return -1;
v = (CFDictionaryRef)CFDictionaryGetValue(props, CFSTR(kIOBlockStorageDriverStatisticsKey));
if(v == NULL){
CFRelease(props);
return -1;
}
ep = &statstab[sizeof(statstab)/sizeof(statstab[0])];
for(bp = &statstab[0]; bp < ep; bp++){
CFStringRef s;
s = CFStringCreateWithCString(kCFAllocatorDefault, bp->key, CFStringGetSystemEncoding());
num = (CFNumberRef)CFDictionaryGetValue(v, s);
if(num)
CFNumberGetValue(num, kCFNumberSInt64Type, ((char*)stat)+bp->off);
CFRelease(s);
}
CFRelease(props);
return 0;
}
+32
View File
@@ -0,0 +1,32 @@
// https://github.com/lufia/iostat/blob/9f7362b77ad333b26c01c99de52a11bdb650ded2/iostat_darwin.h
typedef struct DriveStats DriveStats;
typedef struct CPUStats CPUStats;
enum {
NDRIVE = 16,
NAMELEN = 31
};
struct DriveStats {
char name[NAMELEN+1];
int64_t size;
int64_t blocksize;
int64_t read;
int64_t written;
int64_t nread;
int64_t nwrite;
int64_t readtime;
int64_t writetime;
int64_t readlat;
int64_t writelat;
};
struct CPUStats {
natural_t user;
natural_t nice;
natural_t sys;
natural_t idle;
};
extern int gopsutil_v3_readdrivestat(DriveStats a[], int n);
+1
View File
@@ -1080,6 +1080,7 @@ github.com/shirou/gopsutil/process
# github.com/shirou/gopsutil/v3 v3.22.10
## explicit; go 1.15
github.com/shirou/gopsutil/v3/cpu
github.com/shirou/gopsutil/v3/disk
github.com/shirou/gopsutil/v3/internal/common
github.com/shirou/gopsutil/v3/mem
github.com/shirou/gopsutil/v3/net