5

I am new in GO and mac, and I'm trying to use sysctl for finding the full path name of running processes.

kprocs, err := unix.SysctlKinfoProcSlice("kern.proc.all")
    if err != nil {
        fmt.Println("error: ", err)
    }

    for _, proc := range kprocs {
        name := string(proc.Proc.P_comm[:])
        pid := proc.Proc.P_pid
        extName, err := unix.SysctlKinfoProc("kern.proc.pathname", int(pid))
        if err != nil {
            fmt.Println("error: ", err)
        }

and getting error: no such file or directory

am I using this function correcly?

EDIT If I run the process like this: ./processName ,then I am not getting it's full path, for example /Users/username/go/src/processName - which is what I need. All the solutions with ps will give the relative path, and I need someting that gives the absolute path of the process.

sbs_il
  • 51
  • 2
  • No silly mistake indeed. I've reproduced your error. You're getting all processes info together with the error line right ? (meaning if you comment `extName` there are no errors). – Niloct Aug 22 '22 at 16:54
  • For the processes which get the `ENOENT` error, the `unix.SysctlKinfoProc("kern.proc.pathname", int(pid))` value is `nil`. – Niloct Aug 22 '22 at 17:06
  • See https://stackoverflow.com/a/73191486/152016, change `process.Name()` to `process.Cmdline()` – Niloct Aug 22 '22 at 18:29
  • @niloct, I tried that but, if I run the process like this: ./processName , then I am not getting it's full path, for example /Users/username/go/src/processName - which is what I need – sbs_il Aug 23 '22 at 07:52
  • So your use case is not that you need other processes' info, but only the absolute path of your running golang app ? Then check this: https://stackoverflow.com/a/70491592/152016 – Niloct Aug 23 '22 at 14:02
  • I am getting my own process path from os.Executable(). But I need other processes absolute path to compare to my own process absolute path – sbs_il Aug 24 '22 at 04:33
  • You could call `dirname $(realpath filename)` bash script, for each filename. – Niloct Aug 24 '22 at 13:23
  • If you have the process ID you can use `lsof` to retrieve the full path of the process. With a bit of searching and stripping you get the full path of the command:`PROCESS_ID=123 lsof -p $PROCESS_ID -Fn | awk 'NR==5{print}' | sed "s/n\//\//"`. See also: https://stackoverflow.com/questions/7511864/get-real-path-of-application-from-pid – Eelco Aug 24 '22 at 14:31

2 Answers2

0

Here you go:

func printPath(pid int32) {
    cmd := exec.Command("ps", append([]string{"xuwww", "-p", strconv.Itoa(int(pid))})...)
    output, e := cmd.CombinedOutput()
    if e != nil {
        fmt.Println(e)
    }
    lines := strings.Split(string(output), "\n")
    comps := strings.Fields(lines[1])

    fmt.Println(comps[len(comps)-1])
}

func main() {
    kprocs, err := unix.SysctlKinfoProcSlice("kern.proc.all")
    if err != nil {
        fmt.Println("error: ", err)
    }

    for _, proc := range kprocs {
        pid := proc.Proc.P_pid
        printPath(pid)
    }
}

I didn't use unix.SysctlKinfoProc(), instead ran a shell command ps xuwww -p PID using exec.command(). It returns output as string. Parse the output string to get absolute path.

Output:

649 /System/Library/PrivateFrameworks/IMDPersistence.framework/XPCServices/IMDPersistenceAgent.xpc/Contents/MacOS/IMDPersistenceAgent 646 /System/Library/PrivateFrameworks/AuthKit.framework/Versions/A/Support/akd 643 /System/Library/CoreServices/pbs

Strange
  • 1,460
  • 1
  • 7
  • 18
  • I tried this and I am getting "panic: runtime error: index out of range [-1]" And more then that: If I run the process like this: ./processName , then I am not getting it's full path, for example /Users/username/go/src/processName - which is what I need – sbs_il Aug 23 '22 at 07:41
0

Following my comment on your question you can get the information you are looking for by using lsof.

Similar to the other code example you can iterate over all known processes but for some processes it takes some time for the command to return.

package main

import (
    "fmt"
    "os/exec"

    "golang.org/x/sys/unix"
)

func printPath(pid int32) {
    cmd := exec.Command("bash", "-c", fmt.Sprintf("lsof -p %d -Fn | awk 'NR==5{print}' | sed \"s/n\\//\\//\"", pid))
    output, e := cmd.Output()
    if e != nil {
        fmt.Println(e)
        return
    }
    fmt.Printf("%s", output)
}

func main() {
    kprocs, err := unix.SysctlKinfoProcSlice("kern.proc.all")
    if err != nil {
        fmt.Println("error: ", err)
    }

    for _, proc := range kprocs {
        pid := proc.Proc.P_pid
        fmt.Printf("getting for pid %d: ", pid)
        printPath(pid)
    }
}

The code output can be improved. Processes that have exited already now make the output kind of a mess :-/

Eelco
  • 507
  • 3
  • 18