linuxprocesssystem-callsfile-descriptor

Given a PID FD (as acquired from `pidfd_open`), how does one get the underlying PID for it?


I've looked into the man pages, and there's basically nothing that explains anything, and my web searching has failed.

Man pages for pidfd_*:

I've also looked through other man pages that could have been relevant:

Update: I also tried dereferencing the /proc/self/fd/<PID_FD> symlink, but unfortunately, it just returns anon_inode:[pidfd], which is entirely unhelpful. I initially specified that as an answer, but I later found readlink /proc/self/fd/<PID_FD> wasn't returning the same as readlink /proc/$PID/fd/<PID_FD>, and so I deleted it. If someone can help me out here, bounty's open!


Solution

  • /proc/self/fdinfo contains information about all file descriptors opened by the current process.

    For example, if pidfd is 3, then cat /proc/self/fdinfo/3 can give following information:

        pos:    0
        flags:  02000002
        mnt_id: 15
        ino:    8404
        Pid:    440
        NSpid:  440
    

    where Pid is what we are looking for.

    Here is one implementation of get_pid_for_pidfd

    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/syscall.h>
    #include <unistd.h>
    #define ENOENT 2
    #define ERANGE 34
    int errno;
    static int parse_pid(const char *str, pid_t *pid) {
        unsigned long long int v; char *end; pid_t p;
        errno = 0;
        v = strtoull(str, &end, 0);
        if (end == str) return -ENOENT;
        else if (errno != 0) return -errno;
        p = (pid_t)v;
        if (p < 1 || (unsigned long long int)p != v) return -ERANGE;
        if (pid) *pid = p;
        return 0;
    }
    static int parse_status_field_pid(const char *val, pid_t *pid) {
        const char *t = strrchr(val, '\t');
        if (t == NULL) return -ENOENT;
        return parse_pid(t, pid);
    }
    static int get_pid_for_pidfd(int pidfd, pid_t *pid) {
        char *key = NULL; char *val = NULL; char name[256] = { 0, }; int found = 0;
        FILE *f = NULL; size_t keylen = 0; size_t vallen = 0; ssize_t n; int fd; int r = 0;
        int fdinfo = open("/proc/self/fdinfo", O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY);
        *pid = 0;
        snprintf(name, 256, "%d", pidfd);
        fd = openat(fdinfo, name, O_RDONLY | O_CLOEXEC | O_NOCTTY);
        if (fd != -1) f = fdopen(fd, "r");
        if (f == NULL) return -errno;
        do { n = getdelim(&key, &keylen, ':', f);
            if (n == -1) { r = errno; break; }
            n = getdelim(&val, &vallen, '\n', f);
            if (n == -1) { r = errno; break; }
            if (!strncmp(key, "Pid", 3)) { r = parse_status_field_pid(val, pid); found = r > -1; }
        } while (r == 0 && !found);
        fclose(f);
        if (r < 0) return r; else if (!found) return -ENOENT;
        return 0;
    }
    int main(int argc, char *argv[]) {
        int pidfd; pid_t pid;
        pidfd = syscall(SYS_pidfd_open, atoi(argv[1]), 0);
        if (pidfd == -1) { perror("pidfd_open"); exit(EXIT_FAILURE); }
        if (get_pid_for_pidfd(pidfd, &pid) == 0)
           printf("Input PID is %s, PID returned by get_pid_for_pidfd is %d\n", argv[1], pid);
    }
    

    Compile with :

    gcc -o main main.c
    

    Test with :

    ./main $$