Commit b4846b44 authored by Marco Pfatschbacher's avatar Marco Pfatschbacher

Add support for OpenBSD/amd64

This code is based on the FreeBSD version
and implements roughly the same feature set.
parent 110eb1f0
......@@ -15,12 +15,12 @@ build_test: ## test only buildable
# Supported operating systems
GOOS=linux go test ./... | $(BUILD_FAIL_PATTERN)
GOOS=freebsd go test ./... | $(BUILD_FAIL_PATTERN)
GOOS=openbsd go test ./... | $(BUILD_FAIL_PATTERN)
CGO_ENABLED=0 GOOS=darwin go test ./... | $(BUILD_FAIL_PATTERN)
CGO_ENABLED=1 GOOS=darwin go test ./... | $(BUILD_FAIL_PATTERN)
GOOS=windows go test ./... | $(BUILD_FAIL_PATTERN)
# Operating systems supported for building only (not implemented error if used)
GOOS=dragonfly go test ./... | $(BUILD_FAIL_PATTERN)
GOOS=netbsd go test ./... | $(BUILD_FAIL_PATTERN)
GOOS=openbsd go test ./... | $(BUILD_FAIL_PATTERN)
GOOS=solaris go test ./... | $(BUILD_FAIL_PATTERN)
@echo 'Successfully built on all known operating systems'
// +build !darwin,!linux,!freebsd,!windows
// +build !darwin,!linux,!freebsd,!openbsd,!windows
package cpu
......
// +build openbsd
package cpu
import (
"bytes"
"encoding/binary"
"fmt"
"os/exec"
"strconv"
"strings"
"syscall"
"github.com/shirou/gopsutil/internal/common"
)
// sys/sched.h
const (
CPUser = 0
CPNice = 1
CPSys = 2
CPIntr = 3
CPIdle = 4
CPUStates = 5
)
// sys/sysctl.h
const (
CTLKern = 1 // "high kernel": proc, limits
KernCptime = 40 // KERN_CPTIME
KernCptime2 = 71 // KERN_CPTIME2
)
var ClocksPerSec = float64(128)
func init() {
getconf, err := exec.LookPath("/usr/bin/getconf")
if err != nil {
return
}
out, err := invoke.Command(getconf, "CLK_TCK")
// ignore errors
if err == nil {
i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64)
if err == nil {
ClocksPerSec = float64(i)
}
}
}
func Times(percpu bool) ([]TimesStat, error) {
var ret []TimesStat
var ncpu int
if percpu {
ncpu, _ = Counts(true)
} else {
ncpu = 1
}
for i := 0; i < ncpu; i++ {
var cpuTimes [CPUStates]int64
var mib []int32
if percpu {
mib = []int32{CTLKern, KernCptime}
} else {
mib = []int32{CTLKern, KernCptime2, int32(i)}
}
buf, _, err := common.CallSyscall(mib)
if err != nil {
return ret, err
}
br := bytes.NewReader(buf)
err = binary.Read(br, binary.LittleEndian, &cpuTimes)
if err != nil {
return ret, err
}
c := TimesStat{
User: float64(cpuTimes[CPUser]) / ClocksPerSec,
Nice: float64(cpuTimes[CPNice]) / ClocksPerSec,
System: float64(cpuTimes[CPSys]) / ClocksPerSec,
Idle: float64(cpuTimes[CPIdle]) / ClocksPerSec,
Irq: float64(cpuTimes[CPIntr]) / ClocksPerSec,
}
if !percpu {
c.CPU = "cpu-total"
} else {
c.CPU = fmt.Sprintf("cpu%d", i)
}
ret = append(ret, c)
}
return ret, nil
}
// Returns only one (minimal) CPUInfoStat on OpenBSD
func Info() ([]InfoStat, error) {
var ret []InfoStat
c := InfoStat{}
v, err := syscall.Sysctl("hw.model")
if err != nil {
return nil, err
}
c.ModelName = v
return append(ret, c), nil
}
// +build !darwin,!linux,!freebsd,!windows
// +build !darwin,!linux,!freebsd,!openbsd,!windows
package disk
......
// +build openbsd
package disk
import (
"bytes"
"encoding/binary"
"path"
"syscall"
"unsafe"
"github.com/shirou/gopsutil/internal/common"
)
func Partitions(all bool) ([]PartitionStat, error) {
var ret []PartitionStat
// get length
count, err := syscall.Getfsstat(nil, MNT_WAIT)
if err != nil {
return ret, err
}
fs := make([]Statfs, count)
_, err = Getfsstat(fs, MNT_WAIT)
for _, stat := range fs {
opts := "rw"
if stat.F_flags&MNT_RDONLY != 0 {
opts = "ro"
}
if stat.F_flags&MNT_SYNCHRONOUS != 0 {
opts += ",sync"
}
if stat.F_flags&MNT_NOEXEC != 0 {
opts += ",noexec"
}
if stat.F_flags&MNT_NOSUID != 0 {
opts += ",nosuid"
}
if stat.F_flags&MNT_NODEV != 0 {
opts += ",nodev"
}
if stat.F_flags&MNT_ASYNC != 0 {
opts += ",async"
}
d := PartitionStat{
Device: common.IntToString(stat.F_mntfromname[:]),
Mountpoint: common.IntToString(stat.F_mntonname[:]),
Fstype: common.IntToString(stat.F_fstypename[:]),
Opts: opts,
}
if all == false {
if !path.IsAbs(d.Device) || !common.PathExists(d.Device) {
continue
}
}
ret = append(ret, d)
}
return ret, nil
}
func IOCounters() (map[string]IOCountersStat, error) {
ret := make(map[string]IOCountersStat)
r, err := syscall.Sysctl("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[:])
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)
// Getfsstat is borrowed from pkg/syscall/syscall_freebsd.go
// change Statfs_t to Statfs in order to get more information
func Getfsstat(buf []Statfs, flags int) (n int, err error) {
var _p0 unsafe.Pointer
var bufsize uintptr
if len(buf) > 0 {
_p0 = unsafe.Pointer(&buf[0])
bufsize = unsafe.Sizeof(Statfs{}) * uintptr(len(buf))
}
r0, _, e1 := syscall.Syscall(syscall.SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags))
n = int(r0)
if e1 != 0 {
err = e1
}
return
}
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 Usage(path string) (*UsageStat, error) {
stat := syscall.Statfs_t{}
err := syscall.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 syscall.Statfs_t) string {
return common.IntToString(stat.F_fstypename[:])
}
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_openbsd.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
MNT_RDONLY = 0x00000001
MNT_SYNCHRONOUS = 0x00000002
MNT_NOEXEC = 0x00000004
MNT_NOSUID = 0x00000008
MNT_NODEV = 0x00000010
MNT_ASYNC = 0x00000040
MNT_WAIT = 1
MNT_NOWAIT = 2
MNT_LAZY = 3
)
const (
sizeOfDiskstats = 0x70
)
type (
_C_short int16
_C_int int32
_C_long int64
_C_long_long int64
_C_long_double int64
)
type Statfs struct {
F_flags uint32
F_bsize uint32
F_iosize uint32
Pad_cgo_0 [4]byte
F_blocks uint64
F_bfree uint64
F_bavail int64
F_files uint64
F_ffree uint64
F_favail int64
F_syncwrites uint64
F_syncreads uint64
F_asyncwrites uint64
F_asyncreads uint64
F_fsid Fsid
F_namemax uint32
F_owner uint32
F_ctime uint64
F_fstypename [16]int8
F_mntonname [90]int8
F_mntfromname [90]int8
F_mntfromspec [90]int8
Pad_cgo_1 [2]byte
Mount_info [160]byte
}
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 Fsid struct {
Val [2]int32
}
type Timeval struct {
Sec int64
Usec int64
}
type Diskstat struct{}
type Bintime struct{}
// +build ignore
// Hand writing: _Ctype_struct___0
/*
Input to cgo -godefs.
*/
package disk
/*
#include <sys/types.h>
#include <sys/disk.h>
#include <sys/mount.h>
enum {
sizeofPtr = sizeof(void*),
};
*/
import "C"
// Machine characteristics; for internal use.
const (
sizeofPtr = C.sizeofPtr
sizeofShort = C.sizeof_short
sizeofInt = C.sizeof_int
sizeofLong = C.sizeof_long
sizeofLongLong = C.sizeof_longlong
sizeofLongDouble = C.sizeof_longlong
DEVSTAT_NO_DATA = 0x00
DEVSTAT_READ = 0x01
DEVSTAT_WRITE = 0x02
DEVSTAT_FREE = 0x03
// from sys/mount.h
MNT_RDONLY = 0x00000001 /* read only filesystem */
MNT_SYNCHRONOUS = 0x00000002 /* filesystem written synchronously */
MNT_NOEXEC = 0x00000004 /* can't exec from filesystem */
MNT_NOSUID = 0x00000008 /* don't honor setuid bits on fs */
MNT_NODEV = 0x00000010 /* don't interpret special files */
MNT_ASYNC = 0x00000040 /* filesystem written asynchronously */
MNT_WAIT = 1 /* synchronously wait for I/O to complete */
MNT_NOWAIT = 2 /* start all I/O, but do not wait for it */
MNT_LAZY = 3 /* push data not written by filesystem syncer */
)
const (
sizeOfDiskstats = C.sizeof_struct_diskstats
)
// Basic types
type (
_C_short C.short
_C_int C.int
_C_long C.long
_C_long_long C.longlong
_C_long_double C.longlong
)
type Statfs C.struct_statfs
type Diskstats C.struct_diskstats
type Fsid C.fsid_t
type Timeval C.struct_timeval
type Diskstat C.struct_diskstat
type Bintime C.struct_bintime
// +build !darwin,!linux,!freebsd,!windows
// +build !darwin,!linux,!freebsd,!openbsd,!windows
package host
......
// +build openbsd
package host
import (
"bytes"
"encoding/binary"
"io/ioutil"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"time"
"unsafe"
"github.com/shirou/gopsutil/internal/common"
"github.com/shirou/gopsutil/process"
)
const (
UTNameSize = 32 /* see MAXLOGNAME in <sys/param.h> */
UTLineSize = 8
UTHostSize = 16
)
func Info() (*InfoStat, error) {
ret := &InfoStat{
OS: runtime.GOOS,
PlatformFamily: "openbsd",
}
hostname, err := os.Hostname()
if err == nil {
ret.Hostname = hostname
}
platform, family, version, err := PlatformInformation()
if err == nil {
ret.Platform = platform
ret.PlatformFamily = family
ret.PlatformVersion = version
}
system, role, err := Virtualization()
if err == nil {
ret.VirtualizationSystem = system
ret.VirtualizationRole = role
}
procs, err := process.Pids()
if err == nil {
ret.Procs = uint64(len(procs))
}
boot, err := BootTime()
if err == nil {
ret.BootTime = boot
ret.Uptime = uptime(boot)
}
return ret, nil
}
func BootTime() (uint64, error) {
val, err := common.DoSysctrl("kern.boottime")
if err != nil {
return 0, err
}
boottime, err := strconv.ParseUint(val[0], 10, 64)
if err != nil {
return 0, err
}
return boottime, nil
}
func uptime(boot uint64) uint64 {
return uint64(time.Now().Unix()) - boot
}
func Uptime() (uint64, error) {
boot, err := BootTime()
if err != nil {
return 0, err
}
return uptime(boot), nil
}
func PlatformInformation() (string, string, string, error) {
platform := ""
family := ""
version := ""
uname, err := exec.LookPath("uname")
if err != nil {
return "", "", "", err
}
out, err := invoke.Command(uname, "-s")
if err == nil {
platform = strings.ToLower(strings.TrimSpace(string(out)))
}
out, err = invoke.Command(uname, "-r")
if err == nil {
version = strings.ToLower(strings.TrimSpace(string(out)))
}
return platform, family, version, nil
}
func Virtualization() (string, string, error) {
system := ""
role := ""
return system, role, nil
}
func Users() ([]UserStat, error) {
var ret []UserStat
utmpfile := "/var/run/utmp"
file, err := os.Open(utmpfile)
if err != nil {
return ret, err
}
buf, err := ioutil.ReadAll(file)
if err != nil {
return ret, err
}
u := Utmp{}
entrySize := int(unsafe.Sizeof(u))
count := len(buf) / entrySize
for i := 0; i < count; i++ {
b := buf[i*entrySize : i*entrySize+entrySize]
var u Utmp
br := bytes.NewReader(b)
err := binary.Read(br, binary.LittleEndian, &u)
if err != nil || u.Time == 0 {
continue
}
user := UserStat{
User: common.IntToString(u.Name[:]),
Terminal: common.IntToString(u.Line[:]),
Host: common.IntToString(u.Host[:]),
Started: int(u.Time),
}
ret = append(ret, user)
}
return ret, nil
}
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_openbsd.go
package host
const (
sizeofPtr = 0x8
sizeofShort = 0x2
sizeofInt = 0x4
sizeofLong = 0x8
sizeofLongLong = 0x8
sizeOfUtmp = 0x130
)
type (
_C_short int16
_C_int int32
_C_long int64
_C_long_long int64
)
type Utmp struct {
Line [8]int8
Name [32]int8
Host [256]int8
Time int64
}
type Timeval struct {
Sec int64
Usec int64
}
// +build ignore
/*
Input to cgo -godefs.
*/
package host
/*
#define KERNEL
#include <sys/types.h>
#include <sys/time.h>
#include <utmp.h>
enum {
sizeofPtr = sizeof(void*),
};
*/
import "C"
// Machine characteristics; for internal use.
const (
sizeofPtr = C.sizeofPtr
sizeofShort = C.sizeof_short
sizeofInt = C.sizeof_int
sizeofLong = C.sizeof_long
sizeofLongLong = C.sizeof_longlong
sizeOfUtmp = C.sizeof_struct_utmp
)
// Basic types
type (
_C_short C.short
_C_int C.int
_C_long C.long
_C_long_long C.longlong
)
type Utmp C.struct_utmp
type Timeval C.struct_timeval
// +build freebsd
// +build freebsd openbsd
package common
......
// +build openbsd
package common
import (
"os"
"os/exec"
"strings"
"syscall"
"unsafe"
)
func DoSysctrl(mib string) ([]string, error) {
err := os.Setenv("LC_ALL", "C")
if err != nil {