Say I have 2 processes, ProcessA and ProcessB. If I perform int fd=open(somefile)
in ProcessA, can I then pass the value of file descriptor fd
over IPC to ProcessB and have it manipulate the same file?

- 223,662
- 58
- 417
- 506

- 5,829
- 8
- 27
- 24
-
1Duplicate: http://stackoverflow.com/questions/1997622 – Zitrax Mar 01 '10 at 22:21
-
2See [this question](http://stackoverflow.com/questions/1997622). – bmargulies Mar 01 '10 at 20:10
6 Answers
You can pass a file descriptor to another process over unix domain sockets. Here's the code to pass such a file descriptor, taken from Unix Network Programming
ssize_t
write_fd(int fd, void *ptr, size_t nbytes, int sendfd)
{
struct msghdr msg;
struct iovec iov[1];
#ifdef HAVE_MSGHDR_MSG_CONTROL
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
cmptr = CMSG_FIRSTHDR(&msg);
cmptr->cmsg_len = CMSG_LEN(sizeof(int));
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
*((int *) CMSG_DATA(cmptr)) = sendfd;
#else
msg.msg_accrights = (caddr_t) &sendfd;
msg.msg_accrightslen = sizeof(int);
#endif
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov[0].iov_base = ptr;
iov[0].iov_len = nbytes;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
return(sendmsg(fd, &msg, 0));
}
/* end write_fd */
And here's the code to receive the file descriptor
ssize_t
read_fd(int fd, void *ptr, size_t nbytes, int *recvfd)
{
struct msghdr msg;
struct iovec iov[1];
ssize_t n;
int newfd;
#ifdef HAVE_MSGHDR_MSG_CONTROL
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
#else
msg.msg_accrights = (caddr_t) &newfd;
msg.msg_accrightslen = sizeof(int);
#endif
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov[0].iov_base = ptr;
iov[0].iov_len = nbytes;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
if ( (n = recvmsg(fd, &msg, 0)) <= 0)
return(n);
#ifdef HAVE_MSGHDR_MSG_CONTROL
if ( (cmptr = CMSG_FIRSTHDR(&msg)) != NULL &&
cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
if (cmptr->cmsg_level != SOL_SOCKET)
err_quit("control level != SOL_SOCKET");
if (cmptr->cmsg_type != SCM_RIGHTS)
err_quit("control type != SCM_RIGHTS");
*recvfd = *((int *) CMSG_DATA(cmptr));
} else
*recvfd = -1; /* descriptor was not passed */
#else
/* *INDENT-OFF* */
if (msg.msg_accrightslen == sizeof(int))
*recvfd = newfd;
else
*recvfd = -1; /* descriptor was not passed */
/* *INDENT-ON* */
#endif
return(n);
}
/* end read_fd */

- 223,662
- 58
- 417
- 506
-
22Note though that the actual numerical value of the file descriptor will in general be different in the two processes. – caf Mar 01 '10 at 21:31
-
3You can pass the *number* this way. That doesn't magically mean it will work as a file descriptor to the same file at both ends. – user207421 May 12 '14 at 08:50
-
12@EJP The idea with SCM_RIGHTS is that it will. Though none comes to mind, I'm sure there are some caveats. (i.e., the concept works pretty much like dup(), but between unrelated processes) – nos May 12 '14 at 10:12
If both processes belong the the same user, then you can simply make use of the procfs.
char fd_path[64]; // actual maximal length: 37 for 64bit systems
snprintf(fd_path, sizeof(fd_path), "/proc/%d/fd/%d", SOURCE_PID, SOURCE_FD);
int new_fd = open(fd_path, O_RDWR);
Of course you would need to some IPC mechanism to share the value of SOURCE_FD
. See e.g. “Linux C: upon receiving a signal, is it possible to know the PID of the sender?”.

- 25,517
- 12
- 101
- 143
-
-
3For future readers: this works for real files but definitely does not work for things like un-named domain sockets (`socketpair()`), probably won't work for named domain sockets and I'm not sure about pipes. For domain sockets if you call `open()` on the file system path you'll get `ENXIO`; `connect()` would fail because it only makes sense in the context of an un-connected domain socket; `bind()` will say address already in use. – Brian Vandenberg Feb 02 '21 at 00:21
-
open("/proc/9256/fd/5", O_WRONLY|O_CREAT|O_TRUNC, 0666) = -1 ENXIO (No such device or address) When trying to open a TCP connected socket the way you suggest. open(2) simply doesn't allow for opening a socket file from /proc//fd – Marcelo Pacheco Jul 22 '23 at 19:03
In 2020, on Linux versions 5.6 and above, a new system call was added to Linux that’ll enable a process to obtain a duplicate of a file descriptor of another process referred to by a pidfd with the pidfd_getfd()
system call.

- 31,774
- 17
- 105
- 99
You can use the method nos described in this thread, or the (more conventional) way, by sharing it between related processes (typically parent-child or siblings) by having it created, the forked processes automatically receive a copy.
Indeed, forked processes get all your FDs and can use them unless they close them (which is generally a good idea).
Therefore if a parent forks two children, if they both have a file descriptor they didn't close, it is now shared (even if the parent subsequently closes it). This could, for example, be a pipe from one child to another. This is how shell redirects like
ls -l | more
Work.

- 62,604
- 14
- 116
- 151
Short Answer:
Try pidfd_getfd
Long Answer
The pidfd_getfd() system call allocates a new file descriptor in the calling process (Process B). This new file descriptor is a duplicate of an existing file descriptor, targetfd, in the process (Process A) referred to by the PID file descriptor pidfd . Of course you need a mechanism to get targetfd from Process A.
newfd = syscall(SYS_pidfd_getfd, int pidfd, int targetfd, 0);
We get the PID file descriptor pidfd from pidfd_open().
pidfd = syscall(SYS_pidfd_open, pid_t pid, 0);
The effect of pidfd_getfd() is similar to the use of SCM_RIGHTS messeges, but in order to pass a file descriptor using an SCM_RIGHTS message, the two processes must first establish a UNIX domain socket connection, so requires cooperation on the part of the process whose file descriptor is being copied. By contrast, no such cooperation is necessary when using pidfd_getfd().
A dummy example
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <string.h>
#define MMAP_NAME "/tmp/mmap"
struct shared_mem{
int targetfd;
};
int main(void){
int fd;
struct shared_mem *shmp;
unlink(MMAP_NAME);
fd = open(MMAP_NAME, O_CREAT | O_RDWR, 00600);
ftruncate(fd, sizeof(struct shared_mem));
shmp = (struct shared_mem *)mmap(NULL,
sizeof(struct shared_mem),
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
0);
if (fork() == 0){
sleep(5);
write(syscall(SYS_pidfd_getfd,
syscall(SYS_pidfd_open, getppid(), 0),
shmp->targetfd,
0),
"Messege from Child\n",
strlen("Messege from Child\n"));
close(shmp->targetfd);
exit(EXIT_SUCCESS);
}else{
shmp->targetfd = open("foo.txt", O_RDWR | O_CREAT);
write(shmp->targetfd, "Messege from Parent\n", strlen("Messege from Parent\n"));
wait(NULL);
}
munmap(shmp, sizeof(struct shared_mem));
return EXIT_SUCCESS;
}
..
# cat foo.txt
Messege from Parent
Messege from Child

- 51
- 3
-
Let's assume we can't create the file descriptor before the fork(). – frankscitech May 07 '22 at 14:38
-
Note that in the example of above, the setting of variables when receiving, like:
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov[0].iov_base = ptr;
iov[0].iov_len = nbytes;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
is not required. The whole idea of a message structure with headers is that the receiving site does not have to know what it reads, and can by checking the (first) header, what kind of message it is and what to expect.

- 29
- 1
-
While you’re technically correct, there’s a good reason to specify a buffer in this case: in order to send an OOB message (the socket control message in this case), you need to specify a non-empty message (see unix_stream_sendmsg, e.g. http://lxr.free-electrons.com/source/net/unix/af_unix.c#L1836). When receiving without an iovec, Linux will deliver that message over and over again. Hence, to read more than one OOB messages, you MUST read the message data at some point. – Michael Jan 05 '17 at 18:26