From 90e1c94486bdcc85164a370a2e5abfdafb0c657e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chigot?= Date: Mon, 27 Jan 2020 16:22:21 +0100 Subject: [PATCH] all: add AIX provider 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 --- CHANGELOG.md | 2 + providers/aix/boottime_aix.go | 74 ++++++++++ providers/aix/defs_aix.go | 44 ++++++ providers/aix/host_aix.go | 206 +++++++++++++++++++++++++++ providers/aix/kernel_aix.go | 40 ++++++ providers/aix/machineid_aix.go | 34 +++++ providers/aix/os_aix.go | 81 +++++++++++ providers/aix/process_aix.go | 223 ++++++++++++++++++++++++++++++ providers/aix/ztypes_aix_ppc64.go | 177 ++++++++++++++++++++++++ system.go | 1 + system_test.go | 6 + 11 files changed, 888 insertions(+) create mode 100644 providers/aix/boottime_aix.go create mode 100644 providers/aix/defs_aix.go create mode 100644 providers/aix/host_aix.go create mode 100644 providers/aix/kernel_aix.go create mode 100644 providers/aix/machineid_aix.go create mode 100644 providers/aix/os_aix.go create mode 100644 providers/aix/process_aix.go create mode 100644 providers/aix/ztypes_aix_ppc64.go diff --git a/CHANGELOG.md b/CHANGELOG.md index ee50ac1d..95632c8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/providers/aix/boottime_aix.go b/providers/aix/boottime_aix.go new file mode 100644 index 00000000..2a09304e --- /dev/null +++ b/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") + +} diff --git a/providers/aix/defs_aix.go b/providers/aix/defs_aix.go new file mode 100644 index 00000000..f991557b --- /dev/null +++ b/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 +#include +#include +*/ +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 diff --git a/providers/aix/host_aix.go b/providers/aix/host_aix.go new file mode 100644 index 00000000..9ccea07d --- /dev/null +++ b/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 +} diff --git a/providers/aix/kernel_aix.go b/providers/aix/kernel_aix.go new file mode 100644 index 00000000..5a24f892 --- /dev/null +++ b/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 +} diff --git a/providers/aix/machineid_aix.go b/providers/aix/machineid_aix.go new file mode 100644 index 00000000..833ed84c --- /dev/null +++ b/providers/aix/machineid_aix.go @@ -0,0 +1,34 @@ +// 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" +) + +func MachineID() (string, error) { + output, err := exec.Command("/usr/bin/uname", "-F").Output() + if err != nil { + return "", errors.Wrapf(err, "error while retrieving machine-id") + } + return strings.TrimSpace(string(output)), nil + +} diff --git a/providers/aix/os_aix.go b/providers/aix/os_aix.go new file mode 100644 index 00000000..e363ec65 --- /dev/null +++ b/providers/aix/os_aix.go @@ -0,0 +1,81 @@ +// 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 ( + "io/ioutil" + "strconv" + "strings" + + "github.com/pkg/errors" + + "github.com/elastic/go-sysinfo/types" +) + +func OperatingSystem() (*types.OSInfo, error) { + return getOSInfo() +} + +func getOSInfo() (*types.OSInfo, error) { + version, err := KernelVersion() + if err != nil { + return nil, err + } + + // Parse output of "oslevel -s" + // AIX doesn't really follow major.minor.patch pattern. + // To be as closed as possible, this code associates: + // - major = AIX versions (7.2, 7.1, 6.1, etc) + // Note, as int are wanted, it will be 72, 71 or 61. + // - minor = technical level + // - patch = service pack + var major, minor, patch int + for i, v := range strings.SplitN(version, "-", 4) { + switch i { + case 0: + // "oslevel -s" will return 7200, 7100 or 6100, + // so the last 0 are removed to be more comprehensive. + major, _ = strconv.Atoi(v[0:2]) + case 1: + minor, _ = strconv.Atoi(v) + case 2: + patch, _ = strconv.Atoi(v) + default: + break + } + } + + // Take build version from "/proc/version" rather than the one returned + // "oslevel" as it's more precise. + proc_version, err := ioutil.ReadFile("/proc/version") + if err != nil { + return nil, errors.Wrapf(err, "failed to get OS info: cannot open /proc/version") + } + build := strings.SplitN(string(proc_version), "\n", 4)[2] + + return &types.OSInfo{ + Family: "aix", + Platform: "aix", + Name: "aix", + Version: version, + Major: major, + Minor: minor, + Patch: patch, + Build: build, + }, nil +} diff --git a/providers/aix/process_aix.go b/providers/aix/process_aix.go new file mode 100644 index 00000000..9d278dda --- /dev/null +++ b/providers/aix/process_aix.go @@ -0,0 +1,223 @@ +// 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 ( + "bytes" + "encoding/binary" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/pkg/errors" + + "github.com/elastic/go-sysinfo/types" +) + +func (s aixSystem) Processes() ([]types.Process, error) { + files, err := ioutil.ReadDir("/proc") + if err != nil { + return nil, errors.Wrapf(err, "error while reading /proc") + } + + processes := make([]types.Process, 0, len(files)) + for _, f := range files { + // Check that the file is a correct process directory. + // /proc also contains special files (/proc/version) and threads + // directories (/proc/pid directory but without any "as" file) + if _, err := os.Stat("/proc/" + f.Name() + "/as"); err == nil { + pid, _ := strconv.Atoi(f.Name()) + processes = append(processes, &process{pid: pid}) + } + } + return processes, nil +} + +func (s aixSystem) Process(pid int) (types.Process, error) { + p := process{pid: pid} + return &p, nil +} + +func (s aixSystem) Self() (types.Process, error) { + return s.Process(os.Getpid()) +} + +type process struct { + pid int + info *types.ProcessInfo +} + +func (p *process) PID() int { + return p.pid +} + +func (p *process) Parent() (types.Process, error) { + info, err := p.Info() + if err != nil { + return nil, err + } + return &process{pid: info.PPID}, nil +} + +func (p *process) Info() (types.ProcessInfo, error) { + if p.info != nil { + return *p.info, nil + } + + var psinfo psinfo + if err := p.decodeProcfsFile("psinfo", &psinfo); err != nil { + return types.ProcessInfo{}, err + } + + procdir := "/proc/" + strconv.Itoa(p.pid) + cwd, err := os.Readlink(procdir + "/cwd") + if err != nil { + if !os.IsNotExist(err) { + return types.ProcessInfo{}, errors.Wrapf(err, "error while reading %s/cwd", procdir) + } + } + cwd = strings.TrimSuffix(cwd, "/") + + // psinfo.Psargs is the command launched up to 80 characters, both the executable path + // and its arguments + var exe, name string + args_list := strings.Split(convertBytesToString(psinfo.Psargs[:]), " ") + + // If the first element of the arguments list (ie the executable path) + // is less that the 80 characters, we are sure that the whole name is present. + // Retrieve the name the pathname in psinfo.Psargs + if len(args_list[0]) < 80 { + name = filepath.Base(args_list[0]) + // The process was launched using its absolute path, so we can retrieve + // the executable path from its "name". + if filepath.IsAbs(args_list[0]) { + exe = filepath.Clean(args_list[0]) + } else { + // TODO: improve this case. The executable full path can still + // be retrieve in some cases. Look at os/executable_path.go + // in the stdlib. + // For the moment, let's "exe" be the same as "name" + exe = name + } + } else { + // TODO: parse ps ? + // Use the value of psinfo.Fname, which is the last + // component of the exec()ed pathname up to 16 characters + name = convertBytesToString(psinfo.Fname[:]) + exe = name + } + + p.info = &types.ProcessInfo{ + Name: name, + PID: p.pid, + PPID: int(psinfo.Ppid), + CWD: cwd, + Exe: exe, + Args: args_list, + StartTime: time.Unix(int64(psinfo.Start.Sec), int64(psinfo.Start.Sec)), + } + + return *p.info, nil +} + +func (p *process) User() (types.UserInfo, error) { + var prcred prcred + if err := p.decodeProcfsFile("cred", &prcred); err != nil { + return types.UserInfo{}, err + } + return types.UserInfo{ + UID: strconv.Itoa(int(prcred.Ruid)), + EUID: strconv.Itoa(int(prcred.Euid)), + SUID: strconv.Itoa(int(prcred.Suid)), + GID: strconv.Itoa(int(prcred.Rgid)), + EGID: strconv.Itoa(int(prcred.Egid)), + SGID: strconv.Itoa(int(prcred.Sgid)), + }, nil + +} + +func (p *process) Memory() (types.MemoryInfo, error) { + var mem types.MemoryInfo + pagesize := os.Getpagesize() + + // use "svmon -P pid" output to get memory + // Output should be something similar to: + // (blank line) + // ------------------------------------------------------------------------------- + // Pid Command Inuse Pin Pgsp Virtual 64-bit Mthrd 16MB + // 20054382 sftp-server 36042 17636 0 35984 N N N + out, err := exec.Command("/usr/bin/svmon", "-P", strconv.Itoa(p.pid)).Output() + if err != nil { + return types.MemoryInfo{}, errors.Wrap(err, "failed to get output of svmon") + } + + l := strings.SplitN(string(out), "\n", 4)[3] + for j, s := range strings.Fields(l)[2:5] { + val, err := strconv.Atoi(s) + if err != nil { + return types.MemoryInfo{}, errors.Wrap(err, "failed to parse svmon") + } + if j == 0 { + mem.Resident = uint64(val * pagesize) // inuse + } + if j == 2 { + mem.Virtual = mem.Resident + uint64(val*pagesize) // inuse + pgsp + } + } + + return mem, nil +} + +func (p *process) CPUTime() (types.CPUTimes, error) { + var pstatus pstatus + if err := p.decodeProcfsFile("status", &pstatus); err != nil { + return types.CPUTimes{}, err + } + return types.CPUTimes{ + User: time.Duration(pstatus.Utime.Sec*1e9 + int64(pstatus.Utime.Nsec)), + System: time.Duration(pstatus.Stime.Sec*1e9 + int64(pstatus.Stime.Nsec)), + }, nil +} + +func (p *process) decodeProcfsFile(name string, data interface{}) error { + fileName := "/proc/" + strconv.Itoa(p.pid) + "/" + name + + file, err := os.Open(fileName) + if err != nil { + return errors.Wrapf(err, "error while opening %s", fileName) + } + defer file.Close() + + if err := binary.Read(file, binary.BigEndian, data); err != nil { + return errors.Wrapf(err, "error while decoding %s", fileName) + } + + return nil +} + +func convertBytesToString(arr []byte) string { + n := bytes.IndexByte(arr, 0) + if n == -1 { + return string(arr[:]) + } + return string(arr[:n]) +} diff --git a/providers/aix/ztypes_aix_ppc64.go b/providers/aix/ztypes_aix_ppc64.go new file mode 100644 index 00000000..e22b42cc --- /dev/null +++ b/providers/aix/ztypes_aix_ppc64.go @@ -0,0 +1,177 @@ +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs defs_aix.go + +package aix + +type psinfo struct { + Flag uint32 + Flag2 uint32 + Nlwp uint32 + X_pad1 uint32 + Uid uint64 + Euid uint64 + Gid uint64 + Egid uint64 + Pid uint64 + Ppid uint64 + Pgid uint64 + Sid uint64 + Ttydev uint64 + Addr uint64 + Size uint64 + Rssize uint64 + Start pr_timestruc64 + Time pr_timestruc64 + Cid uint16 + X_pad2 uint16 + Argc uint32 + Argv uint64 + Envp uint64 + Fname [16]uint8 + Psargs [80]uint8 + X_pad [8]uint64 + Lwp lwpsinfo +} +type pr_timestruc64 struct { + Sec int64 + Nsec int32 + X__pad uint32 +} +type lwpsinfo struct { + Lwpid uint64 + Addr uint64 + Wchan uint64 + Flag uint32 + Wtype uint8 + State uint8 + Sname uint8 + Nice uint8 + Pri int32 + Policy uint32 + Clname [8]uint8 + Onpro int32 + Bindpro int32 + Ptid uint32 + X_pad1 uint32 + X_pad [7]uint64 +} + +type prcred struct { + Euid uint64 + Ruid uint64 + Suid uint64 + Egid uint64 + Rgid uint64 + Sgid uint64 + X_pad [8]uint64 + X_pad1 uint32 + Ngroups uint32 + Groups [1]uint64 +} + +type pstatus struct { + Flag uint32 + Flag2 uint32 + Flags uint32 + Nlwp uint32 + Stat uint8 + Dmodel uint8 + X_pad1 [6]uint8 + Sigpend pr_sigset + Brkbase uint64 + Brksize uint64 + Stkbase uint64 + Stksize uint64 + Pid uint64 + Ppid uint64 + Pgid uint64 + Sid uint64 + Utime pr_timestruc64 + Stime pr_timestruc64 + Cutime pr_timestruc64 + Cstime pr_timestruc64 + Sigtrace pr_sigset + Flttrace fltset + Sysentry_offset uint32 + Sysexit_offset uint32 + X_pad [8]uint64 + Lwp lwpstatus +} +type pr_sigset struct { + Set [4]uint64 +} +type fltset struct { + Set [4]uint64 +} +type lwpstatus struct { + Lwpid uint64 + Flags uint32 + X_pad1 [1]uint8 + State uint8 + Cursig uint16 + Why uint16 + What uint16 + Policy uint32 + Clname [8]uint8 + Lwppend pr_sigset + Lwphold pr_sigset + Info pr_siginfo64 + Altstack pr_stack64 + Action pr_sigaction64 + X_pad2 uint32 + Syscall uint16 + Nsysarg uint16 + Sysarg [8]uint64 + Errno int32 + Ptid uint32 + X_pad [9]uint64 + Reg prgregset + Fpreg prfpregset + Family pfamily +} +type pr_siginfo64 struct { + Signo int32 + Errno int32 + Code int32 + Imm int32 + Status int32 + X__pad1 uint32 + Uid uint64 + Pid uint64 + Addr uint64 + Band int64 + Value [8]byte + X__pad [4]uint32 +} +type pr_stack64 struct { + Sp uint64 + Size uint64 + Flags int32 + X__pad [5]int32 +} +type pr_sigaction64 struct { + Union [8]byte + Mask pr_sigset + Flags int32 + X__pad [5]int32 +} +type prgregset struct { + X__iar uint64 + X__msr uint64 + X__cr uint64 + X__lr uint64 + X__ctr uint64 + X__xer uint64 + X__fpscr uint64 + X__fpscrx uint64 + X__gpr [32]uint64 + X__pad1 [8]uint64 +} +type prfpregset struct { + X__fpr [32]float64 +} +type pfamily struct { + Extoff uint64 + Extsize uint64 + Pad [14]uint64 +} diff --git a/system.go b/system.go index 90f81691..baf540bb 100644 --- a/system.go +++ b/system.go @@ -24,6 +24,7 @@ import ( "github.com/elastic/go-sysinfo/types" // Register host and process providers. + _ "github.com/elastic/go-sysinfo/providers/aix" _ "github.com/elastic/go-sysinfo/providers/darwin" _ "github.com/elastic/go-sysinfo/providers/linux" _ "github.com/elastic/go-sysinfo/providers/windows" diff --git a/system_test.go b/system_test.go index 5daabb01..b52e78b5 100644 --- a/system_test.go +++ b/system_test.go @@ -64,6 +64,12 @@ var expectedProcessFeatures = map[string]*ProcessFeatures{ OpenHandleEnumerator: false, OpenHandleCounter: true, }, + "aix": &ProcessFeatures{ + ProcessInfo: true, + Environment: false, + OpenHandleEnumerator: false, + OpenHandleCounter: false, + }, } func TestProcessFeaturesMatrix(t *testing.T) {