Re: Sending FDs over UNIX domain sockets
Pokemon Chw via Python-list <[email protected]>
| Newsgroups | gmane.comp.python.general |
|---|---|
| Message-ID | <[email protected]> |
On Linux AF_UNIX + SOCK_STREAM sockets, there is a quirk in how the kernel handles control messages with SCM_RIGHTS:
To successfully pass file descriptors via SCM_RIGHTS, you must send at least one byte of normal data in the same sendmsg() call. Otherwise, the control message (i.e., the file descriptors) will not actually be transmitted.
Python’s socket.send_fds() / recv_fds() are essentially wrappers around sendmsg / recvmsg. Therefore, if all the buffers you send are empty, the kernel treats the call as having no regular data, and the SCM_RIGHTS control data gets discarded. As a result, the server receives an empty FD list.
Additionally, when you observe that the client prints the file descriptors as 0, 1, 2 but the server receives values such as 5, 6, 7, this is also expected behavior. The numbers 0, 1, and 2 are the file descriptor numbers in the client process (stdin, stdout, stderr). When these FDs are sent using SCM_RIGHTS, the kernel does not send the numeric values themselves; instead, it sends references to the underlying open files. The receiving process then creates new file descriptors, using the lowest available descriptor numbers in its own FD table. Therefore, the actual numeric values on the receiving side will almost always be different (e.g., 5, 6, 7), even though they point to the same underlying file objects.
=== testclient start ===
#!/usr/bin/env python3
from socket import socket, AF_UNIX, SOCK_STREAM, send_fds
import sys
path = '/tmp/test'
s = socket(AF_UNIX, SOCK_STREAM)
s.connect(path)
send_fds(s, [b'x'], [
sys.stdin.fileno(),
sys.stdout.fileno(),
sys.stderr.fileno()
], 3, None)
=== testclient end ===
Fabiano Sidler <[email protected]> 于2025年11月16日周日 03:50写道:
Hi folks!
I'm trying to pass some file descriptors over a UNIX domain socket.
But either I'm doing something wrong, or there is a bug in Python.
Here's my code:
=== testclient start ===
#!/usr/bin/env python3
from socket import socket, AF_UNIX, SOCK_STREAM, send_fds
import sys
path = '/tmp/test'
s = socket(AF_UNIX, SOCK_STREAM)
s.connect(path)
send_fds(s, [bytes()], [
sys.stdin.fileno(),
sys.stdout.fileno(),
sys.stderr.fileno()
], 3, None)
=== testclient end ===
=== testserver start ===
#!/usr/bin/env python3
from socket import socket, AF_UNIX, SOCK_STREAM, recv_fds
from os.path import exists
from os import remove
path = '/tmp/test'
s = socket(AF_UNIX, SOCK_STREAM)
if exists(path):
remove(path)
s.bind(path)
s.listen(-1)
c,_ = s.accept()
fds = recv_fds(c, 1024, 3)
print(fds)
=== testserver end ===
What's the issue?
Best wishes,
Fabiano
--
https://mail.python.org/mailman3//lists/python-list.python.org
--
https://mail.python.org/mailman3//lists/python-list.python.org