5

/proc/net/tcp gives me a local address, port, and inode number for a socket (0.0.0.0:5432 and 9289, for example).

I'd like to find the PID for a specific process, given the above information.

It's possible to open every numbered folder in /proc, and then check symlinks for matching socket/inode numbers with a shell command like "$ sudo ls -l /proc/*/fd/ 2>/dev/null | grep socket". However, this seems more computationally expensive than necessary, since <5% of the processes on any given system have open TCP sockets.

What's the most efficient way to find the PID which has opened a given socket? I'd prefer to use standard libraries, and I'm currently developing with Python 3.2.3.

Edit: Removed the code samples from the question, since they are now included in the answer below.

  • Regarding standard libraries, I'd like to avoid using subprocess.call or os.exec, and do all of the work inside Python natively. – 5ba8cabe450348c7fbe2 Feb 02 '13 at 23:22
  • 1
    There doesn't seem much of an alternative to read procfs, use lsof or use netstat. And I guess all of them do the same (reads procfs). Unfortunately not much else to do. A lot similar SO questions have the same answer: 1. read /proc/net/tcp 2. read /proc//fd/. – emil Feb 03 '13 at 00:04
  • I added some working code using os.readlink and os.listdir - no subprocess calls. The problem I see is that I'm making far more os.readlink calls than necessary - about 600 calls in order to find the PIDs for about four open sockets. – 5ba8cabe450348c7fbe2 Feb 03 '13 at 00:21
  • 1
    Can you narrow down your searches with i.e. particular usernames? If you are interested you can run `strace lsof -n -i`. I don't see why running a subprocess is such a bad thing? This is the unix way. :) – emil Feb 03 '13 at 01:11
  • I'm basically writing a pure Python 3 replacement for netstat (or something similar, at least), so I'm more interested in learning how to do it without subprocess, and then doing that as efficiently as possible. I'm writing from scratch to learn as much as possible about Python 3 and procfs! – 5ba8cabe450348c7fbe2 Feb 03 '13 at 18:54

2 Answers2

6

The following code accomplishes the original goal:

def find_pid(inode):

    # get a list of all files and directories in /proc
    procFiles = os.listdir("/proc/")

    # remove the pid of the current python process
    procFiles.remove(str(os.getpid()))

    # set up a list object to store valid pids
    pids = []

    for f in procFiles:
        try:
            # convert the filename to an integer and back, saving the result to a list
            integer = int(f)
            pids.append(str(integer))
        except ValueError:
            # if the filename doesn't convert to an integer, it's not a pid, and we don't care about it
            pass

    for pid in pids:
        # check the fd directory for socket information
        fds = os.listdir("/proc/%s/fd/" % pid)
        for fd in fds:
            # save the pid for sockets matching our inode
            if ('socket:[%d]' % inode) == os.readlink("/proc/%s/fd/%s" % (pid, fd)):
                return pid
  • 1
    Maybe should do: `if ('socket:[%d]' % inode) == os.readlink("/proc/%s/fd/%s" % (pid, fd)):` instead, so you do not match inodes that inode just is a substring of. But otherwise I think it looks good. Don't know if there is a much more efficient way. – emil Feb 03 '13 at 21:23
  • Thanks for the suggestion - I've edited the code above. I can't think of a way to make this much faster, either, so I'll go ahead and mark it as solved. Thanks! – 5ba8cabe450348c7fbe2 Feb 04 '13 at 21:21
2

I do not know how to do this in python, but you could use lsof(1):

lsof -i | awk -v sock=158384387 '$6 == sock{print $2}'

158384387 is the inode for the socket. And then call it from python using subprocess.Popen.

You will have to use sudo(8) if you want to see sockets opened by other users.

emil
  • 1,642
  • 13
  • 12