Search code examples
clinux

File Descriptor sharing between processes


My goal is to open the device in one process and then close it in another (not fork). I don't want to use UNIX socket. I found that starting kernel 5.6 there are two new syscalls: syscall(SYS_pidfd_open, pid, flags) and syscall(SYS_pidfd_getfd, pidfd, 0, 0).

I tried simple code to check this solution.

open_device.c:

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <errno.h>

#define pidfd_open(pid, flags) syscall(SYS_pidfd_open, pid, flags)

int main() {
    // Open the device
    int fd = open("/dev/null", O_RDWR);
    if (fd == -1) {
        perror("open");
        return EXIT_FAILURE;
    }

    printf("File descriptor for /dev/null: %d\n", fd);

    // Get the current process ID
    pid_t pid = getpid();

    // Obtain a pidfd for this process
    int pidfd = pidfd_open(pid, 0);
    if (pidfd == -1) {
        perror("pidfd_open");
        close(fd);
        return EXIT_FAILURE;
    }

    printf("Generated pidfd: %d\n", pidfd);

    // Write pidfd to a file
    FILE *pidfd_file = fopen("pidfd.txt", "w");
    if (pidfd_file == NULL) {
        perror("fopen");
        close(fd);
        close(pidfd);
        return EXIT_FAILURE;
    }
    fprintf(pidfd_file, "%d\n", pidfd);
    fclose(pidfd_file);

    // Keep the process alive for testing
    while (1) {
        sleep(10);
    }

    // Clean up
    close(fd);
    close(pidfd);

    return 0;
}

close_device.c

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/syscall.h>
#include <errno.h>

#ifndef SYS_pidfd_getfd
#define SYS_pidfd_getfd 438  // Replace with the correct syscall number for your architecture
#endif

int main() {
    // Read the pidfd from the file
    FILE *pidfd_file = fopen("pidfd.txt", "r");
    if (pidfd_file == NULL) {
        perror("fopen");
        return EXIT_FAILURE;
    }

    int pidfd;
    if (fscanf(pidfd_file, "%d", &pidfd) != 1) {
        perror("fscanf");
        fclose(pidfd_file);
        return EXIT_FAILURE;
    }
    fclose(pidfd_file);

    printf("Read pidfd: %d\n", pidfd);

    // Retrieve the file descriptor from the first process
    int target_fd = syscall(SYS_pidfd_getfd, pidfd, 0, 0);
    if (target_fd == -1) {
        perror("pidfd_getfd");
        close(pidfd);
        return EXIT_FAILURE;
    }

    printf("Successfully retrieved fd: %d\n", target_fd);

    // Perform the close operation
    if (close(target_fd) == -1) {
        perror("close");
        close(pidfd);
        return EXIT_FAILURE;
    }

    printf("Device successfully closed.\n");

    close(pidfd);
    return 0;
} 

The problem is that I receive error Bad file descriptor for pidfd_getfd.

Where can be an issue?


Solution

  • You are using the wrong arguments for SYS_pidfd_getfd. pidfd should be a PID descriptor, int pidfd = pidfd_open(remote_pid, 0);, where remote_pid is the process ID of the process which you want to obtain a duplicate file descriptor from.

    You could just write the PID of the opening process down to the same file and then in close_device.c you read that and then call pidfd_open:

    #define _GNU_SOURCE
    #include <errno.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/syscall.h>
    
    #ifndef SYS_pidfd_getfd
    #    define SYS_pidfd_getfd \
            438 // Replace with the correct syscall number for your architecture
    #endif
    
    #define pidfd_open(pid, flags) syscall(SYS_pidfd_open, pid, flags)
    
    int main(void) {
        // Read the pidfd from the file
        FILE* pidfd_file = fopen("pidfd.txt", "r");
        if(pidfd_file == NULL) {
            perror("fopen");
            return EXIT_FAILURE;
        }
    
        pid_t remote_pid;
        int remote_fd;
        // read both pid and the descriptor from the file:
        // Note: %d may not be appropriate for `pid_t`
        if(fscanf(pidfd_file, "%d %d", &remote_pid, &remote_fd) != 2) {
            perror("fscanf");
            fclose(pidfd_file);
            return EXIT_FAILURE;
        }
        fclose(pidfd_file);
        printf("Read pid:%d  remote_fd: %d\n", remote_pid, remote_fd);
    
        // now get the pidfd for the remote process:
        int pidfd = pidfd_open(remote_pid, 0);
        if(pidfd == -1) {
            perror("pidfd_open");
            return EXIT_FAILURE;
        }
    
        // Now you can retrieve the file descriptor from the remote process
        int target_fd = syscall(SYS_pidfd_getfd, pidfd, remote_fd, 0);
        if(target_fd == -1) {
            perror("pidfd_getfd");
            close(pidfd);
            return EXIT_FAILURE;
        }
    
        printf("Successfully retrieved fd: %d\n", target_fd);
    
        // Perform the close operation
        if(close(target_fd) == -1) {
            perror("close");
            return EXIT_FAILURE;
        }
    
        printf("Device successfully closed.\n");
    }