// +build aix package gosigar /* #cgo LDFLAGS: -L/usr/lib -lperfstat #include #include #include #include #include #include #include #include */ import "C" import ( "bytes" "encoding/binary" "fmt" "io" "os" "os/user" "runtime" "strconv" "syscall" "time" "unsafe" ) var system struct { ticks uint64 btime uint64 pagesize uint64 } func init() { // sysconf(_SC_CLK_TCK) returns the number of ticks by second. system.ticks = uint64(C.sysconf(C._SC_CLK_TCK)) system.pagesize = uint64(os.Getpagesize()) } // utmp can't be used by "encoding/binary" if generated by cgo, // some pads will be explicitly missing. type utmp struct { User [256]uint8 ID [14]uint8 Line [64]uint8 XPad1 int16 Pid int32 Type int16 XPad2 int16 Time int64 Termination int16 Exit int16 Host [256]uint8 XdblWordPad int32 XreservedA [2]int32 XreservedV [6]int32 } func bootTime() (uint64, error) { if system.btime != 0 { return system.btime, nil } // Get boot time from /etc/utmp file, err := os.Open("/etc/utmp") if err != nil { return 0, fmt.Errorf("error while opening /etc/utmp: %s", err) } defer file.Close() for { var utmp utmp if err := binary.Read(file, binary.BigEndian, &utmp); err != nil { break } if utmp.Type == C.BOOT_TIME { system.btime = uint64(utmp.Time) break } } return system.btime, nil } func tick2msec(val uint64) uint64 { return val * 1000 / system.ticks } // Get returns the list of file systems func (self *FileSystemList) Get() error { var size C.int _, err := C.mntctl(C.MCTL_QUERY, C.sizeof_int, (*C.char)(unsafe.Pointer(&size))) if err != nil { return fmt.Errorf("error while retrieving file system number: %s", err) } buf := make([]byte, size) num, err := C.mntctl(C.MCTL_QUERY, C.ulong(size), (*C.char)(&buf[0])) if err != nil { return fmt.Errorf("error while retrieving file system list: %s", err) } // Vmount structure has a fixed size area for common data (type, // offsets, etc) and another area with variable length data (devname, // options, etc). These data can be accessed based on the offsets // stored in an array inside the fixed part. They can be retrieve // using index given by C define. vmt2data := func(buf []byte, ent *C.struct_vmount, idx int, baseOff int) []byte { off := int(ent.vmt_data[idx].vmt_off) size := int(ent.vmt_data[idx].vmt_size) return buf[baseOff+off : baseOff+off+size] } entOff := 0 fslist := make([]FileSystem, num) for i := 0; i < int(num); i++ { ent := (*C.struct_vmount)(unsafe.Pointer(&buf[entOff])) fs := &fslist[i] // Correspondances taken for /etc/vfs switch ent.vmt_gfstype { case C.MNT_AIX: fs.SysTypeName = "jfs2" case C.MNT_NAMEFS: fs.SysTypeName = "namefs" case C.MNT_NFS: fs.SysTypeName = "nfs" case C.MNT_JFS: fs.SysTypeName = "jfs" case C.MNT_CDROM: fs.SysTypeName = "cdrom" case C.MNT_PROCFS: fs.SysTypeName = "proc" case C.MNT_SFS: fs.SysTypeName = "sfs" case C.MNT_CACHEFS: fs.SysTypeName = "cachefs" case C.MNT_NFS3: fs.SysTypeName = "nfs3" case C.MNT_AUTOFS: fs.SysTypeName = "autofs" case C.MNT_POOLFS: fs.SysTypeName = "poolfs" case C.MNT_UDF: fs.SysTypeName = "udfs" case C.MNT_NFS4: fs.SysTypeName = "nfs4" case C.MNT_CIFS: fs.SysTypeName = "cifs" case C.MNT_PMEMFS: fs.SysTypeName = "pmemfs" case C.MNT_AHAFS: fs.SysTypeName = "ahafs" case C.MNT_STNFS: fs.SysTypeName = "stnfs" default: if ent.vmt_flags&C.MNT_REMOTE != 0 { fs.SysTypeName = "network" } else { fs.SysTypeName = "none" } } fs.DirName = convertBytesToString(vmt2data(buf, ent, C.VMT_STUB, entOff)) fs.Options = convertBytesToString(vmt2data(buf, ent, C.VMT_ARGS, entOff)) devname := convertBytesToString(vmt2data(buf, ent, C.VMT_OBJECT, entOff)) if ent.vmt_flags&C.MNT_REMOTE != 0 { hostname := convertBytesToString(vmt2data(buf, ent, C.VMT_OBJECT, entOff)) fs.DevName = hostname + ":" + devname } else { fs.DevName = devname } entOff += int(ent.vmt_length) } self.List = fslist return nil } // Get returns the CPU load average func (self *LoadAverage) Get() error { cpudata := C.perfstat_cpu_total_t{} if _, err := C.perfstat_cpu_total(nil, &cpudata, C.sizeof_perfstat_cpu_total_t, 1); err != nil { return fmt.Errorf("perfstat_cpu_total: %s", err) } // from libperfstat.h: // "To calculate the load average, divide the numbers by (1<." fixedToFloat64 := func(x uint64) float64 { return float64(x) / (1 << C.SBITS) } self.One = fixedToFloat64(uint64(cpudata.loadavg[0])) self.Five = fixedToFloat64(uint64(cpudata.loadavg[1])) self.Fifteen = fixedToFloat64(uint64(cpudata.loadavg[2])) return nil } // Get returns the system uptime func (self *Uptime) Get() error { btime, err := bootTime() if err != nil { return err } uptime := time.Now().Sub(time.Unix(int64(btime), 0)) self.Length = uptime.Seconds() return nil } // Get returns the current system memory func (self *Mem) Get() error { meminfo := C.perfstat_memory_total_t{} _, err := C.perfstat_memory_total(nil, &meminfo, C.sizeof_perfstat_memory_total_t, 1) if err != nil { return fmt.Errorf("perfstat_memory_total: %s", err) } self.Total = uint64(meminfo.real_total) * system.pagesize self.Free = uint64(meminfo.real_free) * system.pagesize kern := uint64(meminfo.numperm) * system.pagesize // number of pages in file cache self.Used = self.Total - self.Free self.ActualFree = self.Free + kern self.ActualUsed = self.Used - kern return nil } // Get returns the current system swap memory func (self *Swap) Get() error { ps := C.perfstat_pagingspace_t{} id := C.perfstat_id_t{} id.name[0] = 0 for { // errno can be set during perfstat_pagingspace's call even // if it succeeds. Thus, only check it when the result is -1. if r, err := C.perfstat_pagingspace(&id, &ps, C.sizeof_perfstat_pagingspace_t, 1); r == -1 && err != nil { return fmt.Errorf("perfstat_memory_total: %s", err) } if ps.active != 1 { continue } // convert MB sizes to bytes self.Total += uint64(ps.mb_size) * 1024 * 1024 self.Used += uint64(ps.mb_used) * 1024 * 1024 if id.name[0] == 0 { break } } self.Free = self.Total - self.Used return nil } // Get returns information about a CPU func (self *Cpu) Get() error { cpudata := C.perfstat_cpu_total_t{} if _, err := C.perfstat_cpu_total(nil, &cpudata, C.sizeof_perfstat_cpu_total_t, 1); err != nil { return fmt.Errorf("perfstat_cpu_total: %s", err) } self.User = tick2msec(uint64(cpudata.user)) self.Sys = tick2msec(uint64(cpudata.sys)) self.Idle = tick2msec(uint64(cpudata.idle)) self.Wait = tick2msec(uint64(cpudata.wait)) return nil } // Get returns the list of CPU used by the system func (self *CpuList) Get() error { cpudata := C.perfstat_cpu_t{} id := C.perfstat_id_t{} id.name[0] = 0 // Retrieve the number of cpu using perfstat_cpu capacity, err := C.perfstat_cpu(nil, nil, C.sizeof_perfstat_cpu_t, 0) if err != nil { return fmt.Errorf("error while retrieving CPU number: %s", err) } list := make([]Cpu, 0, capacity) for { if _, err := C.perfstat_cpu(&id, &cpudata, C.sizeof_perfstat_cpu_t, 1); err != nil { return fmt.Errorf("perfstat_cpu: %s", err) } cpu := Cpu{} cpu.User = tick2msec(uint64(cpudata.user)) cpu.Sys = tick2msec(uint64(cpudata.sys)) cpu.Idle = tick2msec(uint64(cpudata.idle)) cpu.Wait = tick2msec(uint64(cpudata.wait)) list = append(list, cpu) if id.name[0] == 0 { break } } self.List = list return nil } // Get returns the list of all active processes func (self *ProcList) Get() error { info := C.struct_procsinfo64{} pid := C.pid_t(0) var list []int for { // getprocs first argument is a void* num, err := C.getprocs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, nil, 0, &pid, 1) if err != nil { return err } list = append(list, int(info.pi_pid)) if num == 0 { break } } self.List = list return nil } // Get returns information about a process func (self *ProcState) Get(pid int) error { info := C.struct_procsinfo64{} cpid := C.pid_t(pid) num, err := C.getprocs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, nil, 0, &cpid, 1) if err != nil { return err } if num != 1 { return syscall.ESRCH } self.Name = C.GoString(&info.pi_comm[0]) self.Ppid = int(info.pi_ppid) self.Pgid = int(info.pi_pgrp) self.Nice = int(info.pi_nice) self.Tty = int(info.pi_ttyd) self.Priority = int(info.pi_pri) switch info.pi_state { case C.SACTIVE: self.State = RunStateRun case C.SIDL: self.State = RunStateIdle case C.SSTOP: self.State = RunStateStop case C.SZOMB: self.State = RunStateZombie case C.SSWAP: self.State = RunStateSleep default: self.State = RunStateUnknown } // Get process username. Fallback to UID if username is not available. uid := strconv.Itoa(int(info.pi_uid)) userID, err := user.LookupId(uid) if err == nil && userID.Username != "" { self.Username = userID.Username } else { self.Username = uid } thrinfo := C.struct_thrdsinfo64{} tid := C.tid_t(0) if _, err := C.getthrds(cpid, unsafe.Pointer(&thrinfo), C.sizeof_struct_thrdsinfo64, &tid, 1); err != nil { self.Processor = int(thrinfo.ti_affinity) } return nil } //Get returns the current memory usage of a process func (self *ProcMem) Get(pid int) error { info := C.struct_procsinfo64{} cpid := C.pid_t(pid) num, err := C.getprocs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, nil, 0, &cpid, 1) if err != nil { return err } if num != 1 { return syscall.ESRCH } self.Size = uint64(info.pi_size) * system.pagesize self.Share = uint64(info.pi_sdsize) * system.pagesize self.Resident = uint64(info.pi_drss+info.pi_trss) * system.pagesize self.MinorFaults = uint64(info.pi_minflt) self.MajorFaults = uint64(info.pi_majflt) self.PageFaults = self.MinorFaults + self.MajorFaults return nil } // Get returns a process uptime func (self *ProcTime) Get(pid int) error { info := C.struct_procsinfo64{} cpid := C.pid_t(pid) num, err := C.getprocs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, nil, 0, &cpid, 1) if err != nil { return err } if num != 1 { return syscall.ESRCH } self.StartTime = uint64(info.pi_start) * 1000 self.User = uint64(info.pi_utime) * 1000 self.Sys = uint64(info.pi_stime) * 1000 self.Total = self.User + self.Sys return nil } // Get returns arguments of a process func (self *ProcArgs) Get(pid int) error { /* If buffer is not large enough, args are truncated */ buf := make([]byte, 8192) info := C.struct_procsinfo64{} info.pi_pid = C.pid_t(pid) if _, err := C.getargs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, (*C.char)(&buf[0]), 8192); err != nil { return err } bbuf := bytes.NewBuffer(buf) var args []string for { arg, err := bbuf.ReadBytes(0) if err == io.EOF || arg[0] == 0 { break } if err != nil { return err } args = append(args, string(chop(arg))) } self.List = args return nil } // Get returns the environment of a process func (self *ProcEnv) Get(pid int) error { if self.Vars == nil { self.Vars = map[string]string{} } /* If buffer is not large enough, args are truncated */ buf := make([]byte, 8192) info := C.struct_procsinfo64{} info.pi_pid = C.pid_t(pid) if _, err := C.getevars(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, (*C.char)(&buf[0]), 8192); err != nil { return err } bbuf := bytes.NewBuffer(buf) delim := []byte{61} // "=" for { line, err := bbuf.ReadBytes(0) if err == io.EOF || line[0] == 0 { break } if err != nil { return err } pair := bytes.SplitN(chop(line), delim, 2) if len(pair) != 2 { return fmt.Errorf("Error reading process environment for PID: %d", pid) } self.Vars[string(pair[0])] = string(pair[1]) } return nil } // Get returns the path of the process executable func (self *ProcExe) Get(pid int) error { /* If buffer is not large enough, args are truncated */ buf := make([]byte, 8192) info := C.struct_procsinfo64{} info.pi_pid = C.pid_t(pid) if _, err := C.getargs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, (*C.char)(&buf[0]), 8192); err != nil { return err } bbuf := bytes.NewBuffer(buf) // retrieve the first argument cmd, err := bbuf.ReadBytes(0) if err != nil { return err } self.Name = string(chop(cmd)) cwd, err := os.Readlink("/proc/" + strconv.Itoa(pid) + "/cwd") if err != nil { if !os.IsNotExist(err) { return err } } self.Cwd = cwd return nil } // Get returns process filesystem usage. Not implimented on AIX. func (*ProcFDUsage) Get(_ int) error { return ErrNotImplemented{runtime.GOOS} } // Get returns filesytem usage. Not implimented on AIX. func (*FDUsage) Get() error { return ErrNotImplemented{runtime.GOOS} } // Get returns huge pages info. Not implimented on AIX. func (*HugeTLBPages) Get() error { return ErrNotImplemented{runtime.GOOS} }