@@ -5,13 +5,39 @@ package process
55
66// #include <stdlib.h>
77// #include <libproc.h>
8+ // #include <string.h>
9+ // #include <sys/errno.h>
10+ // #include <sys/proc_info.h>
11+ // #include <sys/sysctl.h>
812import "C"
913import (
14+ "bytes"
1015 "context"
1116 "fmt"
17+ "strings"
18+ "syscall"
1219 "unsafe"
1320)
1421
22+ var argMax int
23+
24+ func init () {
25+ argMax = getArgMax ()
26+ }
27+
28+ func getArgMax () int {
29+ var (
30+ mib = [... ]C.int {C .CTL_KERN , C .KERN_ARGMAX }
31+ argmax C.int
32+ size C.size_t = C .ulong (unsafe .Sizeof (argmax ))
33+ )
34+ retval := C .sysctl (& mib [0 ], 2 , unsafe .Pointer (& argmax ), & size , C .NULL , 0 )
35+ if retval == 0 {
36+ return int (argmax )
37+ }
38+ return 0
39+ }
40+
1541func (p * Process ) ExeWithContext (ctx context.Context ) (string , error ) {
1642 var c C.char // need a var for unsafe.Sizeof need a var
1743 const bufsize = C .PROC_PIDPATHINFO_MAXSIZE * unsafe .Sizeof (c )
@@ -28,3 +54,86 @@ func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
2854
2955 return C .GoString (buffer ), nil
3056}
57+
58+ // CwdWithContext retrieves the Current Working Directory for the given process.
59+ // It uses the proc_pidinfo from libproc and will only work for processes the
60+ // EUID can access. Otherwise "operation not permitted" will be returned as the
61+ // error.
62+ // Note: This might also work for other *BSD OSs.
63+ func (p * Process ) CwdWithContext (ctx context.Context ) (string , error ) {
64+ const vpiSize = C .sizeof_struct_proc_vnodepathinfo
65+ vpi := (* C .struct_proc_vnodepathinfo )(C .malloc (vpiSize ))
66+ defer C .free (unsafe .Pointer (vpi ))
67+ ret , err := C .proc_pidinfo (C .int (p .Pid ), C .PROC_PIDVNODEPATHINFO , 0 , unsafe .Pointer (vpi ), vpiSize )
68+ if err != nil {
69+ // fmt.Printf("ret: %d %T\n", ret, err)
70+ if err == syscall .EPERM {
71+ return "" , ErrorNotPermitted
72+ }
73+ return "" , err
74+ }
75+ if ret <= 0 {
76+ return "" , fmt .Errorf ("unknown error: proc_pidinfo returned %d" , ret )
77+ }
78+ if ret != C .sizeof_struct_proc_vnodepathinfo {
79+ return "" , fmt .Errorf ("too few bytes; expected %d, got %d" , vpiSize , ret )
80+ }
81+ return C .GoString (& vpi .pvi_cdir .vip_path [0 ]), err
82+ }
83+
84+ func procArgs (pid int32 ) (* []byte , int , error ) {
85+ var (
86+ mib = [... ]C.int {C .CTL_KERN , C .KERN_PROCARGS2 , C .int (pid )}
87+ size C.size_t = C .ulong (argMax )
88+ nargs C.int
89+ result []byte
90+ )
91+ procargs := (* C .char )(C .malloc (C .ulong (argMax )))
92+ defer C .free (unsafe .Pointer (procargs ))
93+ retval := C .sysctl (& mib [0 ], 3 , unsafe .Pointer (procargs ), & size , C .NULL , 0 )
94+ if retval == 0 {
95+ C .memcpy (unsafe .Pointer (& nargs ), unsafe .Pointer (procargs ), C .sizeof_int )
96+ result = C .GoBytes (unsafe .Pointer (procargs ), C .int (size ))
97+ // fmt.Printf("size: %d %d\n%s\n", size, nargs, hex.Dump(result))
98+ return & result , int (nargs ), nil
99+ }
100+ return nil , 0 , fmt .Errorf ("error: %d" , retval )
101+ }
102+
103+ func (p * Process ) CmdlineSliceWithContext (ctx context.Context ) ([]string , error ) {
104+ pargs , nargs , err := procArgs (p .Pid )
105+ if err != nil {
106+ return nil , err
107+ }
108+ // The first bytes hold the nargs int, skip it.
109+ args := bytes .Split ((* pargs )[C .sizeof_int :], []byte {0 })
110+ var argStr string
111+ // The first element is the actual binary/command path.
112+ // command := args[0]
113+ var argSlice []string
114+ // var envSlice []string
115+ // All other, non-zero elements are arguments. The first "nargs" elements
116+ // are the arguments. Everything else in the slice is then the environment
117+ // of the process.
118+ for _ , arg := range args [1 :] {
119+ argStr = string (arg [:])
120+ if len (argStr ) > 0 {
121+ if nargs > 0 {
122+ argSlice = append (argSlice , argStr )
123+ nargs --
124+ continue
125+ }
126+ break
127+ // envSlice = append(envSlice, argStr)
128+ }
129+ }
130+ return argSlice , err
131+ }
132+
133+ func (p * Process ) CmdlineWithContext (ctx context.Context ) (string , error ) {
134+ r , err := p .CmdlineSliceWithContext (ctx )
135+ if err != nil {
136+ return "" , err
137+ }
138+ return strings .Join (r , " " ), err
139+ }
0 commit comments