feat: add MethodCall::push_fd() for passing file descriptors (SCM_RIGHTS) - #149
Conversation
…HTS) Varlink allows a method call to carry file descriptors as ancillary data (SCM_RIGHTS), referenced from the parameters by push order. The client side had no way to attach them. Add MethodCall::push_fd(fd) (unix only): it dups the fd (F_DUPFD_CLOEXEC, so the caller keeps ownership), queues it, and returns its push-order index. send() then delivers the queued fds with the request via a single sendmsg()/SCM_RIGHTS on the connection's socket (any tail past the first sendmsg is written normally). Only socket-backed connections (Connection::with_address) support this; a reader/writer-pair connection has no socket and push_fd() returns an Unsupported error. This is send-only; receiving fds is not implemented. Tested in varlink/src/test.rs: push_fd() over a socketpair, recvmsg() on the peer confirming the descriptor arrives and refers to the same underlying pipe, plus the Unsupported case for non-socket connections.
Review:
|
- Resolve the `sent` flag via cfg instead of a mutable binding: on non-unix builds the only mutation was inside #[cfg(unix)], tripping clippy's unused_mut under -D warnings on windows CI. - Reject non-AF_UNIX sockets at push_fd() time (getsockname family check). SCM_RIGHTS only exists on AF_UNIX; previously a tcp: connection accepted the fd and failed later in sendmsg() with EINVAL. Docs updated to say AF_UNIX-socket-backed. - Docs: fd passing is not part of the core varlink protocol; it is systemd's sd-varlink extension, and push_fd mirrors sd_varlink_push_fd(3). - Tests: TCP connection rejected with Unsupported; multiple push_fd calls return consecutive indices and arrive as one SCM_RIGHTS control message with push order preserved; fd queue is cleared on send, so a subsequent send carries no ancillary data.
|
Thanks for the thorough review, @haraldh — all points addressed in 0aa089b:
On the opt-in gating: I'd argue the client side doesn't need one — calling
|
Coverage Report for CI Build 30100250885Warning No base build found for commit Coverage: 56.305%Details
Uncovered Changes
Coverage RegressionsRequires a base build to compare against. How to fix this → Coverage Stats
💛 - Coveralls |
|
Thank you! |
systemd's
sd-varlinkextends varlink with file-descriptor passing over theUnix socket via SCM_RIGHTS: fds are pushed before the call and referenced from
the parameters by their push-order index. (This is not part of the core varlink
protocol as specified on varlink.org — the push-order convention is the de-facto
one used by systemd's services.) This crate had no client-side way to attach
fds; this adds that, mirroring
sd_varlink_push_fd().API
MethodCall::push_fd(fd) -> io::Result<usize>(unix only). Dups the fd withF_DUPFD_CLOEXEC(caller keeps ownership of the original), queues it, andreturns its push-order index.
sendmsg()+SCM_RIGHTSon the connection's socket; any bytes past the firstsendmsgare written normally through the existing writer.AF_UNIX-socket-backed connections support this — SCM_RIGHTS does notexist elsewhere.
push_fdchecks the socket family up front (getsockname),so a
tcp:connection or a reader/writer-pair connection fails immediatelywith
io::ErrorKind::Unsupportedrather than later at send time.No opt-in gate (cf. systemd's
SD_VARLINK_SERVER_ALLOW_FD_PASSING_OUTPUT):on the client side, calling
push_fdis itself the explicit opt-in — noancillary data is ever attached unless the application pushed an fd for that
specific call.
Scope
Send-only. Receiving descriptors (server → client) is not implemented — it would
require recvmsg-based reading throughout the input path, and nothing needs it yet.
cfg(unix)only, matching the crate's existing platform split;libcis alreadya unix dependency.
Tests
varlink/src/test.rs:test_push_fd_passes_descriptor: push an fd over one end of a socketpair,recvmsg()the other end, and confirm the descriptor arrives and refers to thesame underlying pipe (write through the received fd, read it back).
test_push_fd_multiple_and_cleared_after_send: two fds arrive in oneSCM_RIGHTS control message with consecutive indices and push order preserved;
the queue is cleared on send, so a second send carries no ancillary data.
test_push_fd_requires_socket:push_fdon a non-socket connection returnsUnsupported.test_push_fd_requires_unix_socket:push_fdon a TCP-backed connectionreturns
Unsupported.cargo test -p varlinkpasses.