diff --git a/process/process_darwin.go b/process/process_darwin.go index 414ea98..c728bcd 100644 --- a/process/process_darwin.go +++ b/process/process_darwin.go @@ -81,6 +81,9 @@ func (p *Process) Name() (string, error) { func (p *Process) Exe() (string, error) { return "", common.NotImplementedError } + +// Cmdline returns the command line arguments of the process as a string with +// each argument separated by 0x20 ascii character. func (p *Process) Cmdline() (string, error) { r, err := callPs("command", p.Pid, false) if err != nil { @@ -88,6 +91,19 @@ func (p *Process) Cmdline() (string, error) { } return strings.Join(r[0], " "), err } + +// CmdlineSlice 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) CmdlineSlice() ([]string, error) { + r, err := callPs("command", p.Pid, false) + if err != nil { + return nil, err + } + return r[0], err +} func (p *Process) CreateTime() (int64, error) { return 0, common.NotImplementedError } diff --git a/process/process_freebsd.go b/process/process_freebsd.go index 9835bc9..3242ff6 100644 --- a/process/process_freebsd.go +++ b/process/process_freebsd.go @@ -54,6 +54,7 @@ func (p *Process) Name() (string, error) { func (p *Process) Exe() (string, error) { return "", common.NotImplementedError } + func (p *Process) Cmdline() (string, error) { mib := []int32{CTLKern, KernProc, KernProcArgs, p.Pid} buf, _, err := common.CallSyscall(mib) @@ -69,6 +70,27 @@ func (p *Process) Cmdline() (string, error) { return strings.Join(ret, " "), nil } + +func (p *Process) CmdlineSlice() ([]string, error) { + mib := []int32{CTLKern, KernProc, KernProcArgs, p.Pid} + buf, _, err := common.CallSyscall(mib) + if err != nil { + return nil, err + } + if len(buf) == 0 { + return nil, nil + } + if buf[len(buf)-1] == 0 { + buf = buf[:len(buf)-1] + } + parts := bytes.Split(buf, []byte{0}) + var strParts []string + for _, p := range parts { + strParts = append(strParts, string(p)) + } + + return strParts, nil +} func (p *Process) CreateTime() (int64, error) { return 0, common.NotImplementedError } diff --git a/process/process_linux.go b/process/process_linux.go index a160e4d..833a298 100644 --- a/process/process_linux.go +++ b/process/process_linux.go @@ -3,6 +3,7 @@ package process import ( + "bytes" "encoding/json" "errors" "fmt" @@ -91,9 +92,19 @@ func (p *Process) Name() (string, error) { func (p *Process) Exe() (string, error) { return p.fillFromExe() } + +// Cmdline returns the command line arguments of the process as a string with +// each argument separated by 0x20 ascii character. func (p *Process) Cmdline() (string, error) { return p.fillFromCmdline() } + +// CmdlineSlice returns the command line arguments of the process as a slice with each +// element being an argument. +func (p *Process) CmdlineSlice() ([]string, error) { + return p.fillSliceFromCmdline() +} + func (p *Process) CreateTime() (int64, error) { _, _, _, createTime, _, err := p.fillFromStat() if err != nil { @@ -410,6 +421,28 @@ func (p *Process) fillFromCmdline() (string, error) { return strings.Join(ret, " "), nil } +func (p *Process) fillSliceFromCmdline() ([]string, error) { + pid := p.Pid + cmdPath := common.HostProc(strconv.Itoa(int(pid)), "cmdline") + cmdline, err := ioutil.ReadFile(cmdPath) + if err != nil { + return nil, err + } + if len(cmdline) == 0 { + return nil, nil + } + if cmdline[len(cmdline)-1] == 0 { + cmdline = cmdline[:len(cmdline)-1] + } + parts := bytes.Split(cmdline, []byte{0}) + var strParts []string + for _, p := range parts { + strParts = append(strParts, string(p)) + } + + return strParts, nil +} + // Get IO status from /proc/(pid)/io func (p *Process) fillFromIO() (*IOCountersStat, error) { pid := p.Pid diff --git a/process/process_test.go b/process/process_test.go index 129204f..c5aac11 100644 --- a/process/process_test.go +++ b/process/process_test.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "os/user" + "reflect" "runtime" "strings" "sync" @@ -120,6 +121,18 @@ func Test_Process_CmdLine(t *testing.T) { } } +func Test_Process_CmdLineSlice(t *testing.T) { + p := testGetProcess() + + v, err := p.CmdlineSlice() + if err != nil { + t.Fatalf("geting cmdline slice error %v", err) + } + if !reflect.DeepEqual(v, os.Args) { + t.Errorf("returned cmdline slice not as expected:\nexp: %v\ngot: %v", os.Args, v) + } +} + func Test_Process_Ppid(t *testing.T) { p := testGetProcess() diff --git a/process/process_windows.go b/process/process_windows.go index 51a0ee1..f4fe119 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -5,6 +5,7 @@ package process import ( "errors" "fmt" + "strings" "syscall" "time" "unsafe" @@ -145,6 +146,17 @@ func (p *Process) Cmdline() (string, error) { return *dst[0].CommandLine, nil } +// CmdlineSlice returns the command line arguments of the process as a slice with each +// element being an argument. This merely returns the CommandLine informations passed +// to the process split on the 0x20 ASCII character. +func (p *Process) CmdlineSlice() ([]string, error) { + cmdline, err := p.Cmdline() + if err != nil { + return nil, err + } + return strings.Split(cmdline, " "), nil +} + func (p *Process) CreateTime() (int64, error) { dst, err := GetWin32Proc(p.Pid) if err != nil {