mirror of
https://github.com/shirou/gopsutil.git
synced 2025-05-14 19:29:16 +08:00
Additional Darwin CGO implementation
- added CwdWithContext to get the current working dir of a process - added CmdLineWithContext natively w/ support for spaces etc.
This commit is contained in:
parent
ec072d99db
commit
9494ebdd94
@ -218,7 +218,7 @@ name x x x x x
|
||||
cmdline x x x x
|
||||
create_time x x x
|
||||
status x x x x
|
||||
cwd x
|
||||
cwd x x
|
||||
exe x x x x
|
||||
uids x x x x
|
||||
gids x x x x
|
||||
|
@ -20,6 +20,7 @@ var (
|
||||
invoke common.Invoker = common.Invoke{}
|
||||
ErrorNoChildren = errors.New("process does not have children")
|
||||
ErrorProcessNotRunning = errors.New("process does not exist")
|
||||
ErrorNotPermitted = errors.New("operation not permitted")
|
||||
)
|
||||
|
||||
type Process struct {
|
||||
|
@ -19,10 +19,6 @@ func (p *Process) TgidWithContext(ctx context.Context) (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
@ -73,4 +69,3 @@ func parseKinfoProc(buf []byte) (KinfoProc, error) {
|
||||
err := common.Read(br, binary.LittleEndian, &k)
|
||||
return k, err
|
||||
}
|
||||
|
||||
|
@ -100,14 +100,6 @@ func (p *Process) NameWithContext(ctx context.Context) (string, error) {
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
|
||||
r, err := callPsWithContext(ctx, "command", p.Pid, false, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.Join(r[0], " "), err
|
||||
}
|
||||
|
||||
// cmdNameWithContext returns the command name (including spaces) without any arguments
|
||||
func (p *Process) cmdNameWithContext(ctx context.Context) ([]string, error) {
|
||||
r, err := callPsWithContext(ctx, "command", p.Pid, false, true)
|
||||
@ -117,19 +109,6 @@ func (p *Process) cmdNameWithContext(ctx context.Context) ([]string, error) {
|
||||
return r[0], err
|
||||
}
|
||||
|
||||
// CmdlineSliceWithContext returns the command line arguments of the process as a slice with each
|
||||
// element being an argument. Because of current deficiencies in the way that the command
|
||||
// line arguments are found, single arguments that have spaces in the will actually be
|
||||
// reported as two separate items. In order to do something better CGO would be needed
|
||||
// to use the native darwin functions.
|
||||
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
|
||||
r, err := callPsWithContext(ctx, "command", p.Pid, false, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r[0], err
|
||||
}
|
||||
|
||||
func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
|
||||
r, err := callPsWithContext(ctx, "etime", p.Pid, false, false)
|
||||
if err != nil {
|
||||
|
@ -5,13 +5,39 @@ package process
|
||||
|
||||
// #include <stdlib.h>
|
||||
// #include <libproc.h>
|
||||
// #include <string.h>
|
||||
// #include <sys/errno.h>
|
||||
// #include <sys/proc_info.h>
|
||||
// #include <sys/sysctl.h>
|
||||
import "C"
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var argMax int
|
||||
|
||||
func init() {
|
||||
argMax = getArgMax()
|
||||
}
|
||||
|
||||
func getArgMax() int {
|
||||
var (
|
||||
mib = [...]C.int{C.CTL_KERN, C.KERN_ARGMAX}
|
||||
argmax C.int
|
||||
size C.size_t = C.ulong(unsafe.Sizeof(argmax))
|
||||
)
|
||||
retval := C.sysctl(&mib[0], 2, unsafe.Pointer(&argmax), &size, C.NULL, 0)
|
||||
if retval == 0 {
|
||||
return int(argmax)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
|
||||
var c C.char // need a var for unsafe.Sizeof need a var
|
||||
const bufsize = C.PROC_PIDPATHINFO_MAXSIZE * unsafe.Sizeof(c)
|
||||
@ -28,3 +54,86 @@ func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
|
||||
|
||||
return C.GoString(buffer), nil
|
||||
}
|
||||
|
||||
// CwdWithContext retrieves the Current Working Directory for the given process.
|
||||
// It uses the proc_pidinfo from libproc and will only work for processes the
|
||||
// EUID can access. Otherwise "operation not permitted" will be returned as the
|
||||
// error.
|
||||
// Note: This might also work for other *BSD OSs.
|
||||
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
|
||||
const vpiSize = C.sizeof_struct_proc_vnodepathinfo
|
||||
vpi := (*C.struct_proc_vnodepathinfo)(C.malloc(vpiSize))
|
||||
defer C.free(unsafe.Pointer(vpi))
|
||||
ret, err := C.proc_pidinfo(C.int(p.Pid), C.PROC_PIDVNODEPATHINFO, 0, unsafe.Pointer(vpi), vpiSize)
|
||||
if err != nil {
|
||||
// fmt.Printf("ret: %d %T\n", ret, err)
|
||||
if err == syscall.EPERM {
|
||||
return "", ErrorNotPermitted
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
if ret <= 0 {
|
||||
return "", fmt.Errorf("unknown error: proc_pidinfo returned %d", ret)
|
||||
}
|
||||
if ret != C.sizeof_struct_proc_vnodepathinfo {
|
||||
return "", fmt.Errorf("too few bytes; expected %d, got %d", vpiSize, ret)
|
||||
}
|
||||
return C.GoString(&vpi.pvi_cdir.vip_path[0]), err
|
||||
}
|
||||
|
||||
func procArgs(pid int32) (*[]byte, int, error) {
|
||||
var (
|
||||
mib = [...]C.int{C.CTL_KERN, C.KERN_PROCARGS2, C.int(pid)}
|
||||
size C.size_t = C.ulong(argMax)
|
||||
nargs C.int
|
||||
result []byte
|
||||
)
|
||||
procargs := (*C.char)(C.malloc(C.ulong(argMax)))
|
||||
defer C.free(unsafe.Pointer(procargs))
|
||||
retval := C.sysctl(&mib[0], 3, unsafe.Pointer(procargs), &size, C.NULL, 0)
|
||||
if retval == 0 {
|
||||
C.memcpy(unsafe.Pointer(&nargs), unsafe.Pointer(procargs), C.sizeof_int)
|
||||
result = C.GoBytes(unsafe.Pointer(procargs), C.int(size))
|
||||
// fmt.Printf("size: %d %d\n%s\n", size, nargs, hex.Dump(result))
|
||||
return &result, int(nargs), nil
|
||||
}
|
||||
return nil, 0, fmt.Errorf("error: %d", retval)
|
||||
}
|
||||
|
||||
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
|
||||
pargs, nargs, err := procArgs(p.Pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// The first bytes hold the nargs int, skip it.
|
||||
args := bytes.Split((*pargs)[C.sizeof_int:], []byte{0})
|
||||
var argStr string
|
||||
// The first element is the actual binary/command path.
|
||||
// command := args[0]
|
||||
var argSlice []string
|
||||
// var envSlice []string
|
||||
// All other, non-zero elements are arguments. The first "nargs" elements
|
||||
// are the arguments. Everything else in the slice is then the environment
|
||||
// of the process.
|
||||
for _, arg := range args[1:] {
|
||||
argStr = string(arg[:])
|
||||
if len(argStr) > 0 {
|
||||
if nargs > 0 {
|
||||
argSlice = append(argSlice, argStr)
|
||||
nargs--
|
||||
continue
|
||||
}
|
||||
break
|
||||
// envSlice = append(envSlice, argStr)
|
||||
}
|
||||
}
|
||||
return argSlice, err
|
||||
}
|
||||
|
||||
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
|
||||
r, err := p.CmdlineSliceWithContext(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.Join(r, " "), err
|
||||
}
|
||||
|
@ -9,8 +9,14 @@ import (
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/shirou/gopsutil/v3/internal/common"
|
||||
)
|
||||
|
||||
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
|
||||
lsof_bin, err := exec.LookPath("lsof")
|
||||
if err != nil {
|
||||
@ -32,3 +38,24 @@ func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
|
||||
}
|
||||
return "", fmt.Errorf("missing txt data returned by lsof")
|
||||
}
|
||||
|
||||
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
|
||||
r, err := callPsWithContext(ctx, "command", p.Pid, false, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.Join(r[0], " "), err
|
||||
}
|
||||
|
||||
// CmdlineSliceWithContext returns the command line arguments of the process as a slice with each
|
||||
// element being an argument. Because of current deficiencies in the way that the command
|
||||
// line arguments are found, single arguments that have spaces in the will actually be
|
||||
// reported as two separate items. In order to do something better CGO would be needed
|
||||
// to use the native darwin functions.
|
||||
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
|
||||
r, err := callPsWithContext(ctx, "command", p.Pid, false, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r[0], err
|
||||
}
|
||||
|
@ -64,6 +64,10 @@ func (p *Process) NameWithContext(ctx context.Context) (string, error) {
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
@ -69,6 +69,10 @@ func (p *Process) NameWithContext(ctx context.Context) (string, error) {
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user