Skip to content

Commit

Permalink
all: add AIX provider
Browse files Browse the repository at this point in the history
Add a implementation without CGO for AIX.
Host information are retrieved using AIX commands (uname, oslevel)
Process information are retrieved by /proc folder.

A CGO implementation could be added to more accurate syscalls and libraries
(getprocs, libperfstat, etc). But github.com/elastic/gosigar provides such
things.

A few features are still missing:
 - Host CPU usage since boot time: there is no easy way to retrieve it.
 - Process' environment
  • Loading branch information
Clément Chigot committed Jan 27, 2020
1 parent b719bc4 commit 90e1c94
Show file tree
Hide file tree
Showing 11 changed files with 888 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Add AIX support [#76](https://github.com/elastic/go-sysinfo/pull/76)

### Changed

### Deprecated
Expand Down
74 changes: 74 additions & 0 deletions providers/aix/boottime_aix.go
@@ -0,0 +1,74 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package aix

import (
"encoding/binary"
"os"
"time"

"github.com/pkg/errors"
)

// utmp can't be used by "encoding/binary" if generated by cgo,
// some pads will be missing.
type utmp struct {
User [256]uint8
Id [14]uint8
Line [64]uint8
X_Pad1 int16
Pid int32
Type int16
X_Pad2 int16
Time int64
Termination int16
Exit int16
Host [256]uint8
X__dbl_word_pad int32
X__reservedA [2]int32
X__reservedV [6]int32
}

const (
BOOT_TIME = 2
)

// BootTime returns the time at which the machine was started, truncated to the nearest second
func BootTime() (time.Time, error) {
// Get boot time from /etc/utmp
file, err := os.Open("/etc/utmp")
if err != nil {
return time.Time{}, errors.Wrapf(err, "failed to get host uptime: cannot open /etc/utmp")
}

defer file.Close()

for {
var utmp utmp
if err := binary.Read(file, binary.BigEndian, &utmp); err != nil {
break
}

if utmp.Type == BOOT_TIME {
return time.Unix(utmp.Time, 0), nil
}
}

return time.Time{}, errors.Wrapf(err, "failed to get host uptime: no utmp record")

}
44 changes: 44 additions & 0 deletions providers/aix/defs_aix.go
@@ -0,0 +1,44 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

// +build ignore

package aix

/*
#include <sys/types.h>
#include <utmp.h>
#include <sys/procfs.h>
*/
import "C"

type psinfo C.psinfo_t
type pr_timestruc64 C.pr_timestruc64_t
type lwpsinfo C.lwpsinfo_t

type prcred C.prcred_t

type pstatus C.pstatus_t
type pr_sigset C.pr_sigset_t
type fltset C.fltset_t
type lwpstatus C.lwpstatus_t
type pr_siginfo64 C.pr_siginfo64_t
type pr_stack64 C.pr_stack64_t
type pr_sigaction64 C.struct_pr_sigaction64
type prgregset C.prgregset_t
type prfpregset C.prfpregset_t
type pfamily C.pfamily_t
206 changes: 206 additions & 0 deletions providers/aix/host_aix.go
@@ -0,0 +1,206 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package aix

import (
"os"
"os/exec"
"strconv"
"strings"
"time"

"github.com/joeshaw/multierror"
"github.com/pkg/errors"

"github.com/elastic/go-sysinfo/internal/registry"
"github.com/elastic/go-sysinfo/providers/shared"
"github.com/elastic/go-sysinfo/types"
)

//go:generate sh -c "go tool cgo -godefs defs_aix.go | sed 's/*byte/uint64/g' > ztypes_aix_ppc64.go"
// As cgo will return some psinfo's fields with *byte, binary.Read will refuse this type.

func init() {
registry.Register(aixSystem{})
}

type aixSystem struct{}

func (s aixSystem) Host() (types.Host, error) {
return newHost()
}

type host struct {
info types.HostInfo
}

func Architecture() (string, error) {
return "ppc", nil
}

func (h *host) Info() types.HostInfo {
return h.info
}

func (h *host) CPUTime() (types.CPUTimes, error) {
return types.CPUTimes{}, types.ErrNotImplemented
}

func (h *host) Memory() (*types.HostMemoryInfo, error) {
var mem types.HostMemoryInfo

pagesize := os.Getpagesize()

// Use "svmon -G" output to get memory
// Output should be something similar to:
// size inuse free pin virtual mmode
// memory 2621440 2440556 180884 701953 790488 Ded
// pg space 2097152 3707
// ...
out, err := exec.Command("/usr/bin/svmon", "-G").Output()
if err != nil {
return nil, errors.Wrap(err, "failed to get output of svmon")
}
var svmon_matrix [2][6]int
for i, l := range strings.SplitN(string(out), "\n", 4)[1:3] {
for j, s := range strings.Fields(l)[1:] {
// Remove mmode
if i == 0 && j == 5 {
continue
}
// Because of the space between "pg" and "space", they will be
// two different columns.
if i == 1 && j == 0 {
continue
}
svmon_matrix[i][j], err = strconv.Atoi(s)
if err != nil {
return nil, errors.Wrap(err, "failed to parse svmon")
}
}
}

mem.Total = uint64(svmon_matrix[0][0] * pagesize) // memory/size
mem.Used = uint64(svmon_matrix[0][1] * pagesize) // memory/inuse
mem.Free = uint64(svmon_matrix[0][2] * pagesize) // memory/free

// There is no real equivalent to memory available in AIX.
mem.Available = mem.Free

mem.VirtualTotal = mem.Total + uint64(svmon_matrix[1][1]*pagesize) // memory/size + pg space/size
mem.VirtualUsed = mem.Used + uint64(svmon_matrix[1][2]*pagesize) // memory/inuse + pg space/inuse
mem.VirtualFree = mem.VirtualTotal - mem.VirtualUsed

return &mem, nil
}

func newHost() (*host, error) {
h := &host{}
r := &reader{}
r.architecture(h)
r.bootTime(h)
r.hostname(h)
r.network(h)
r.kernelVersion(h)
r.os(h)
r.time(h)
r.uniqueID(h)
return h, r.Err()
}

type reader struct {
errs []error
}

func (r *reader) addErr(err error) bool {
if err != nil {
if errors.Cause(err) != types.ErrNotImplemented {
r.errs = append(r.errs, err)
}
return true
}
return false
}

func (r *reader) Err() error {
if len(r.errs) > 0 {
return &multierror.MultiError{Errors: r.errs}
}
return nil
}

func (r *reader) architecture(h *host) {
v, err := Architecture()
if r.addErr(err) {
return
}
h.info.Architecture = v
}

func (r *reader) bootTime(h *host) {
v, err := BootTime()
if r.addErr(err) {
return
}
h.info.BootTime = v
}

func (r *reader) hostname(h *host) {
v, err := os.Hostname()
if r.addErr(err) {
return
}
h.info.Hostname = v
}

func (r *reader) network(h *host) {
ips, macs, err := shared.Network()
if r.addErr(err) {
return
}
h.info.IPs = ips
h.info.MACs = macs
}

func (r *reader) kernelVersion(h *host) {
v, err := KernelVersion()
if r.addErr(err) {
return
}
h.info.KernelVersion = v
}

func (r *reader) os(h *host) {
v, err := OperatingSystem()
if r.addErr(err) {
return
}
h.info.OS = v
}

func (r *reader) time(h *host) {
h.info.Timezone, h.info.TimezoneOffsetSec = time.Now().Zone()
}

func (r *reader) uniqueID(h *host) {
v, err := MachineID()
if r.addErr(err) {
return
}
h.info.UniqueID = v
}
40 changes: 40 additions & 0 deletions providers/aix/kernel_aix.go
@@ -0,0 +1,40 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package aix

import (
"os/exec"
"strings"

"github.com/pkg/errors"
)

var oslevel string

func KernelVersion() (string, error) {
if oslevel != "" {
return oslevel, nil
}

out, err := exec.Command("/usr/bin/oslevel", "-s").Output()
if err != nil {
return "", errors.Wrap(err, "kernel version")
}
oslevel = strings.TrimSuffix(string(out), "\n")
return oslevel, nil
}

0 comments on commit 90e1c94

Please sign in to comment.