metric/system/process/process_aix.go (146 lines of code) (raw):

// 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 process import ( "bytes" "errors" "fmt" "io" "os" "os/user" "strconv" "syscall" "unsafe" "github.com/elastic/elastic-agent-libs/mapstr" "github.com/elastic/elastic-agent-libs/opt" "github.com/elastic/elastic-agent-system-metrics/metric/system/resolve" ) /* #include <procinfo.h> #include <sys/types.h> */ import "C" // FetchPids returns a map and array of pids func (procStats *Stats) FetchPids() (ProcsMap, []ProcState, error) { info := C.struct_procsinfo64{} pid := C.pid_t(0) procMap := make(ProcsMap, 0) var wrappedErr error var plist []ProcState 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 nil, nil, fmt.Errorf("error fetching PIDs: %w", err) } procMap, plist, err = procStats.pidIter(int(pid), procMap, plist) wrappedErr = errors.Join(wrappedErr, err) if num == 0 { break } } return procMap, plist, toNonFatal(wrappedErr) } // GetInfoForPid returns basic info for the process func GetInfoForPid(_ resolve.Resolver, pid int) (ProcState, 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 ProcState{}, fmt.Errorf("error in getprocs: %w", err) } if num != 1 { return ProcState{}, syscall.ESRCH } state := ProcState{} state.Pid = opt.IntWith(pid) state.Name = C.GoString(&info.pi_comm[0]) state.Ppid = opt.IntWith(int(info.pi_ppid)) state.Pgid = opt.IntWith(int(info.pi_pgrp)) switch info.pi_state { case C.SACTIVE: state.State = Running case C.SIDL: state.State = Idle case C.SSTOP: state.State = Stopped case C.SZOMB: state.State = Zombie case C.SSWAP: state.State = Sleeping default: state.State = Unknown } // 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 != "" { state.Username = userID.Username } else { state.Username = uid } return state, nil } // FillPidMetrics is the aix implementation func FillPidMetrics(_ resolve.Resolver, pid int, state ProcState, filter func(string) bool) (ProcState, error) { pagesize := uint64(os.Getpagesize()) 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 state, fmt.Errorf("error in getprocs: %w", err) } if num != 1 { return state, syscall.ESRCH } state.Memory.Size = opt.UintWith(uint64(info.pi_size) * pagesize) state.Memory.Share = opt.UintWith(uint64(info.pi_sdsize) * pagesize) state.Memory.Rss.Bytes = opt.UintWith(uint64(info.pi_drss+info.pi_trss) * pagesize) state.CPU.StartTime = unixTimeMsToTime(uint64(info.pi_start) * 1000) state.CPU.User.Ticks = opt.UintWith(uint64(info.pi_utime) * 1000) state.CPU.System.Ticks = opt.UintWith(uint64(info.pi_stime) * 1000) state.CPU.Total.Ticks = opt.UintWith(opt.SumOptUint(state.CPU.User.Ticks, state.CPU.System.Ticks)) // Get Proc Args /* If buffer is not large enough, args are truncated */ buf := make([]byte, 8192) 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 state, fmt.Errorf("error in getargs: %w", 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 state, fmt.Errorf("error reading args buffer: %w", err) } args = append(args, stripNullByte(arg)) } state.Args = args state.Exe = args[0] // get env vars buf = make([]byte, 8192) if _, err := C.getevars(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, (*C.char)(&buf[0]), 8192); err != nil { return state, fmt.Errorf("error in getevars: %w", err) } if state.Env != nil { return state, nil } bbuf = bytes.NewBuffer(buf) delim := []byte{61} // "=" vars := mapstr.M{} for { line, err := bbuf.ReadBytes(0) if errors.Is(err, io.EOF) || line[0] == 0 { break } if err != nil { return state, fmt.Errorf("error: %w", err) } pair := bytes.SplitN(stripNullByteRaw(line), delim, 2) if len(pair) != 2 { return state, fmt.Errorf("error reading environment: %w", err) } eKey := string(pair[0]) if filter == nil || filter(eKey) { vars[string(pair[0])] = string(pair[1]) } } state.Env = vars return state, nil } func FillMetricsRequiringMoreAccess(_ int, state ProcState) (ProcState, error) { return state, nil } func GetSelfPid(hostfs resolve.Resolver) (int, error) { return os.Getpid(), nil }