#!/usr/bin/env dub
/+ dub.sdl:
name "io_uring_sendmsg_recvmsg"
dependency "during" version="~>0.5.0"
platforms "linux"
targetPath "build"
+/
/**
* `io_uring` — network message ops `IORING_OP_SENDMSG` / `IORING_OP_RECVMSG`
* (Linux 5.3), demonstrated with `SCM_RIGHTS` file-descriptor passing.
*
* 5.3 taught `io_uring` to drive the full `sendmsg(2)`/`recvmsg(2)` scatter/gather
* interface, including the ancillary-data (control-message) channel. This example
* uses that channel for its most famous trick: passing an open file descriptor
* across a `AF_UNIX` socket via an `SCM_RIGHTS` control message.
*
* The flow:
* 1. Open a temp file and `socketpair(AF_UNIX)`.
* 2. Queue a `RECVMSG` on one end and a `SENDMSG` on the other; the send carries
* a `cmsghdr{ SOL_SOCKET, SCM_RIGHTS }` whose payload is the temp file's fd.
* 3. Submit both with one `io_uring_enter`, reap both CQEs.
* 4. The kernel installs a *new* fd in this process pointing at the same open
* file description. We `fstat(2)` both and confirm `st_ino`/`st_dev` match —
* proof the descriptor really crossed the socket through the ring.
*
* Companion to the io_uring chronology:
* see docs/research/async-io/io-uring/timeline.md § "5.3 — Network message ops".
*
* Run with: `dub run --single sendmsg-recvmsg.d`
*
* Portability: prints `SKIP:` and exits 0 if `io_uring` is unavailable or if the
* kernel rejects SEND/RECVMSG (`-EINVAL`/`-EOPNOTSUPP`); 6.18 supports both.
*/
module (module) io_uring_sendmsg_recvmsgio_uring — network message ops IORING_OP_SENDMSG / IORING_OP_RECVMSG
(Linux 5.3), demonstrated with SCM_RIGHTS file-descriptor passing.
5.3 taught io_uring to drive the full sendmsg(2)/recvmsg(2) scatter/gather
interface, including the ancillary-data (control-message) channel. This example
uses that channel for its most famous trick: passing an open file descriptor
across a AF_UNIX socket via an SCM_RIGHTS control message.
The flow:
Open a temp file and socketpair(AF_UNIX).
Queue a RECVMSG on one end and a SENDMSG on the other; the send carries
a cmsghdr{ SOL_SOCKET, SCM_RIGHTS } whose payload is the temp file's fd.
Submit both with one io_uring_enter, reap both CQEs.
The kernel installs a new fd in this process pointing at the same open
file description. We fstat(2) both and confirm st_ino/st_dev match —
proof the descriptor really crossed the socket through the ring.
Companion to the io_uring chronology:
see docs/research/async-io/io-uring/timeline.md § "5.3 — Network message ops".
Run with: dub run --single sendmsg-recvmsg.d
Portability
prints SKIP: and exits 0 if io_uring is unavailable or if the
kernel rejects SEND/RECVMSG (-EINVAL/-EOPNOTSUPP); 6.18 supports both.
io_uring_sendmsg_recvmsg;
import (module) duringSimple idiomatic dlang wrapper around linux io_uring
(see: https://kernel.dk/io_uring.pdf) asynchronous API.
during;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.errnoD header file for C99.
pubs.opengroup.org/onlinepubs/009695399/basedefs/errno.h.html, errno.h
Source
core/stdc/errno.d
errno : (alias constant) io_uring_sendmsg_recvmsg.EINVAL = int core.stdc.errno.EINVAL = 22EINVAL, (alias constant) io_uring_sendmsg_recvmsg.EOPNOTSUPP = int core.stdc.errno.EOPNOTSUPP = 95EOPNOTSUPP, (alias constant) io_uring_sendmsg_recvmsg.ENOSYS = int core.stdc.errno.ENOSYS = 38ENOSYS;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(module) core.sys.posix.fcntlD header file for POSIX.
fcntl : open, (alias constant) io_uring_sendmsg_recvmsg.O_RDWR = int core.sys.posix.fcntl.O_RDWR = 2O_RDWR, (alias constant) io_uring_sendmsg_recvmsg.O_CREAT = int core.sys.posix.fcntl.O_CREAT = 64O_CREAT, (alias constant) io_uring_sendmsg_recvmsg.O_TRUNC = int core.sys.posix.fcntl.O_TRUNC = 512O_TRUNC;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(package) core.sys.posix.syssys.(module) core.sys.posix.sys.socketD header file for POSIX.
socket :
(alias) io_uring_sendmsg_recvmsg.socketpair = int core.sys.posix.sys.socket.socketpair(int, int, int, ref int[2]) nothrow @nogc @safesocketpair, (alias enum value) io_uring_sendmsg_recvmsg.AF_UNIX = core.sys.posix.sys.socket.AF_UNIX = 1AF_UNIX, (alias enum value) io_uring_sendmsg_recvmsg.SOCK_STREAM = core.sys.posix.sys.socket.SOCK_STREAM = 1SOCK_STREAM, (alias enum value) io_uring_sendmsg_recvmsg.SOL_SOCKET = core.sys.posix.sys.socket.SOL_SOCKET = 1SOL_SOCKET, (alias enum value) io_uring_sendmsg_recvmsg.SCM_RIGHTS = core.sys.posix.sys.socket.SCM_RIGHTS = 1uSCM_RIGHTS,
(struct) core.sys.posix.sys.socket.msghdrmsghdr, (struct) core.sys.posix.sys.socket.cmsghdrcmsghdr, (alias) io_uring_sendmsg_recvmsg.CMSG_FIRSTHDR = inout(core.sys.posix.sys.socket.cmsghdr)* core.sys.posix.sys.socket.CMSG_FIRSTHDR(inout(core.sys.posix.sys.socket.msghdr)* mhdr) pure nothrow @nogcCMSG_FIRSTHDR, (alias) io_uring_sendmsg_recvmsg.CMSG_DATA = inout(ubyte)* core.sys.posix.sys.socket.CMSG_DATA(return scope inout(core.sys.posix.sys.socket.cmsghdr)* cmsg) pure nothrow @nogcCMSG_DATA, (alias) io_uring_sendmsg_recvmsg.CMSG_SPACE = ulong core.sys.posix.sys.socket.CMSG_SPACE(ulong len) pure nothrow @nogcCMSG_SPACE, (alias template) io_uring_sendmsg_recvmsg.CMSG_LEN = core.sys.posix.sys.socket.CMSG_LEN()(size_t len)CMSG_LEN;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(package) core.sys.posix.syssys.(module) core.sys.posix.sys.statD header file for POSIX.
stat : (struct) core.sys.posix.sys.stat.stat_tstat_t, fstat;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(package) core.sys.posix.syssys.(module) core.sys.posix.sys.uioD header file for POSIX.
uio : (struct) core.sys.posix.sys.uio.ioveciovec;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(module) core.sys.posix.unistdD header file for POSIX.
unistd : (alias) io_uring_sendmsg_recvmsg.close = int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose, (alias) io_uring_sendmsg_recvmsg.unlink = int core.sys.posix.unistd.unlink(scope const(char*)) nothrow @nogcunlink, (alias) io_uring_sendmsg_recvmsg.write = long core.sys.posix.unistd.write(int, scope const(void*), ulong) nothrow @nogcwrite;
import (package) stdstd.(module) std.stdioCategory Symbols File handles _popen File isFileHandle openNetwork stderr stdin stdout Reading chunks lines readf readfln readln Writing toFile write writef writefln writeln Misc KeepTerminator LockType StdioException
Standard I/O functions that extend core.stdc.stdio. core.stdc.stdio
is publically imported when importing std.stdio.
There are three layers of I/O:
The lowest layer is the operating system layer. The two main schemes are Windows and Posix.
C's stdio.h which unifies the two operating system schemes.
std.stdio, this module, unifies the various stdio.h implementations into
a high level package for D programs.
Source
std/stdio.d
stdio : (alias template) io_uring_sendmsg_recvmsg.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))Equivalent to writef(fmt, args, '\n').
writefln, stderr;
import (package) stdstd.(module) std.stringString handling functions.
Category Functions Searching column indexOf indexOfAny indexOfNeither lastIndexOf lastIndexOfAny lastIndexOfNeither Comparison isNumeric Mutation capitalize Pruning and Filling center chomp chompPrefix chop detabber detab entab entabber leftJustify outdent rightJustify strip stripLeft stripRight wrap Substitution abbrev soundex soundexer succ tr translate Miscellaneous assumeUTF fromStringz lineSplitter representation splitLines toStringz Objects of types string, wstring, and dstring are value types and cannot be mutated element-by-element. For using mutation during building strings, use char[], wchar[], or dchar[]. The xxxstring types are preferable because they don't exhibit undesired aliasing, thus making code more robust.
The following functions are publicly imported:
Module Functions Publicly imported functions std.algorithm cmp, std,algorithm,comparison count, std,algorithm,searching endsWith, std,algorithm,searching startsWith, std,algorithm,searching std.array join, std,array replace, std,array replaceInPlace, std,array split, std,array empty, std,array std.format format, std,format sformat, std,format std.uni icmp, std,uni toLower, std,uni toLowerInPlace, std,uni toUpper, std,uni toUpperInPlace, std,uni There is a rich set of functions for string handling defined in other modules. Functions related to Unicode and ASCII are found in std.uni and std.ascii, respectively. Other functions that have a wider generality than just strings can be found in std.algorithm and std.range.
Source
std/string.d
string : (alias) io_uring_sendmsg_recvmsg.toStringz = immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trustedtoStringz;
int int D main()main()
{
(struct) during.UringMain entry point to work with io_uring.
It hides SubmissionQueue and CompletionQueue behind standard range interface.
We put in SubmissionEntry entries and take out CompletionEntry entries.
Use predefined prepXX methods to fill required fields of SubmissionEntry before put or during putWith.
Note
prepXX functions doesn't touch previous entry state, just fills in operation properties. This is because for
less error prone interface it is cleared automatically when prepared using putWith. So when using on own SubmissionEntry
(outside submission queue), that would be added to the submission queue using put, be sure its cleared if it's
reused for multiple operations.
Uring (local variable) during.Uring ioio;
const (local variable) const(int) setupRetsetupRet = (local variable) during.Uring ioio.int during.setup(ref during.Uring uring, uint entries = 128u, during.io_uring.SetupFlags flags = SetupFlags.NONE) nothrow @nogc @safeSetup new instance of io_uring into provided Uring structure.
setup(8);
if ((local variable) const(int) setupRetsetupRet < 0)
{
void std.stdio.writefln!(char, const(int))(in char[] fmt, const(int) __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("SKIP: io_uring_setup failed (errno %d) — io_uring unavailable on this host", -(local variable) const(int) setupRetsetupRet);
return 0;
}
// ---- A real on-disk file whose fd we will hand to our peer -------------
enum (constant) string io_uring_sendmsg_recvmsg.main.path = "/tmp/io_uring_scm_rights_demo.tmp"path = "/tmp/io_uring_scm_rights_demo.tmp";
const int (local variable) const(int) srcFdsrcFd = open(int core.sys.posix.fcntl.open64(scope const(char*), int, ...) nothrow @nogcpath.immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trustedExamples
import core.stdc.string : strlen;
import std.conv : to;
auto p = toStringz("foo");
assert(strlen(p) == 3);
const(char)[] foo = "abbzxyzzy";
p = toStringz(foo[3 .. 5]);
assert(strlen(p) == 2);
string test = "";
p = toStringz(test);
assert(*p == 0);
test = "\0";
p = toStringz(test);
assert(*p == 0);
test = "foo\0";
p = toStringz(test);
assert(p[0] == 'f' && p[1] == 'o' && p[2] == 'o' && p[3] == 0);
const string test2 = "";
p = toStringz(test2);
assert(*p == 0);
assert(toStringz([]) is toStringz(""));
toStringz, (constant) int core.sys.posix.fcntl.O_RDWR = 2O_RDWR | (constant) int core.sys.posix.fcntl.O_CREAT = 64O_CREAT | (constant) int core.sys.posix.fcntl.O_TRUNC = 512O_TRUNC, (template instance) io_uring_sendmsg_recvmsg.octal!"600"octal!"600");
if ((local variable) const(int) srcFdsrcFd < 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("open(%s) failed", (constant) string io_uring_sendmsg_recvmsg.main.path = "/tmp/io_uring_scm_rights_demo.tmp"path);
return 1;
}
scope (exit) { int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) const(int) srcFdsrcFd); int core.sys.posix.unistd.unlink(scope const(char*)) nothrow @nogcunlink((constant) string io_uring_sendmsg_recvmsg.main.path = "/tmp/io_uring_scm_rights_demo.tmp"path.immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trustedExamples
import core.stdc.string : strlen;
import std.conv : to;
auto p = toStringz("foo");
assert(strlen(p) == 3);
const(char)[] foo = "abbzxyzzy";
p = toStringz(foo[3 .. 5]);
assert(strlen(p) == 2);
string test = "";
p = toStringz(test);
assert(*p == 0);
test = "\0";
p = toStringz(test);
assert(*p == 0);
test = "foo\0";
p = toStringz(test);
assert(p[0] == 'f' && p[1] == 'o' && p[2] == 'o' && p[3] == 0);
const string test2 = "";
p = toStringz(test2);
assert(*p == 0);
assert(toStringz([]) is toStringz(""));
toStringz); }
long core.sys.posix.unistd.write(int, scope const(void*), ulong) nothrow @nogcwrite((local variable) const(int) srcFdsrcFd, "io_uring".(constant) immutable(char)* "io_uring".ptr = "io_uring"ptr, 8); // give the file some content / a real inode
(struct) core.sys.posix.sys.stat.stat_tstat_t (local variable) core.sys.posix.sys.stat.stat_t srcStatsrcStat;
if (fstat(int core.sys.posix.sys.stat.fstat64(int, core.sys.posix.sys.stat.stat_t*) nothrow @nogc @trustedsrcFd, &(local variable) core.sys.posix.sys.stat.stat_t srcStatsrcStat) != 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("fstat(srcFd) failed");
return 1;
}
// ---- The transport: a connected pair of AF_UNIX stream sockets ---------
int[2] (local variable) int[2] socksock;
if (int core.sys.posix.sys.socket.socketpair(int, int, int, ref int[2]) nothrow @nogc @safesocketpair((enum value) core.sys.posix.sys.socket.AF_UNIX = 1AF_UNIX, (enum value) core.sys.posix.sys.socket.SOCK_STREAM = 1SOCK_STREAM, 0, (local variable) int[2] socksock) != 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("socketpair() failed");
return 1;
}
scope (exit) { int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) int[2] socksock[0]); int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) int[2] socksock[1]); }
// A 1-byte normal payload rides alongside the ancillary fd. SCM_RIGHTS
// messages must carry at least one data byte, otherwise the kernel may drop
// the ancillary data.
ubyte[1] (local variable) ubyte[1] sendDatasendData = [0x2A];
ubyte[1] (local variable) ubyte[1] recvDatarecvData = [0x00];
(struct) core.sys.posix.sys.uio.ioveciovec (local variable) core.sys.posix.sys.uio.iovec sendIovsendIov = (struct) core.sys.posix.sys.uio.ioveciovec((local variable) ubyte[1] sendDatasendData.(constant) ubyte* ubyte[1].ptr = &sendDataptr, (local variable) ubyte[1] sendDatasendData.(constant) ulong ubyte[1].length = 1LUlength);
(struct) core.sys.posix.sys.uio.ioveciovec (local variable) core.sys.posix.sys.uio.iovec recvIovrecvIov = (struct) core.sys.posix.sys.uio.ioveciovec((local variable) ubyte[1] recvDatarecvData.(constant) ubyte* ubyte[1].ptr = &recvDataptr, (local variable) ubyte[1] recvDatarecvData.(constant) ulong ubyte[1].length = 1LUlength);
// ---- Control-message buffers (the SCM_RIGHTS channel) ------------------
// CMSG_SPACE(int.sizeof) reserves room for one cmsghdr + an aligned int fd.
enum (alias) object.size_t = ulongsize_t (constant) ulong io_uring_sendmsg_recvmsg.main.controlLen = 24LUcontrolLen = ulong core.sys.posix.sys.socket.CMSG_SPACE(ulong len) pure nothrow @nogcCMSG_SPACE(int.(constant) ulong int.sizeof = 4LUsizeof);
ubyte[(constant) ulong io_uring_sendmsg_recvmsg.main.controlLen = 24LUcontrolLen] (local variable) ubyte[24] sendControlsendControl = 0;
ubyte[(constant) ulong io_uring_sendmsg_recvmsg.main.controlLen = 24LUcontrolLen] (local variable) ubyte[24] recvControlrecvControl = 0;
(struct) core.sys.posix.sys.socket.msghdrmsghdr (local variable) core.sys.posix.sys.socket.msghdr sendMsgsendMsg;
(local variable) core.sys.posix.sys.socket.msghdr sendMsgsendMsg.(field) core.sys.posix.sys.uio.iovec* core.sys.posix.sys.socket.msghdr.msg_iovmsg_iov = &(local variable) core.sys.posix.sys.uio.iovec sendIovsendIov;
(local variable) core.sys.posix.sys.socket.msghdr sendMsgsendMsg.(field) ulong core.sys.posix.sys.socket.msghdr.msg_iovlenmsg_iovlen = 1;
(local variable) core.sys.posix.sys.socket.msghdr sendMsgsendMsg.(field) void* core.sys.posix.sys.socket.msghdr.msg_controlmsg_control = (local variable) ubyte[24] sendControlsendControl.(constant) ubyte* ubyte[24].ptr = &sendControlptr;
(local variable) core.sys.posix.sys.socket.msghdr sendMsgsendMsg.(field) ulong core.sys.posix.sys.socket.msghdr.msg_controllenmsg_controllen = (constant) ulong io_uring_sendmsg_recvmsg.main.controlLen = 24LUcontrolLen;
// Fill the single control message: level SOL_SOCKET, type SCM_RIGHTS,
// payload = the fd we want the peer to receive.
(struct) core.sys.posix.sys.socket.cmsghdrcmsghdr* (local variable) core.sys.posix.sys.socket.cmsghdr* cmcm = inout(core.sys.posix.sys.socket.cmsghdr)* core.sys.posix.sys.socket.CMSG_FIRSTHDR(inout(core.sys.posix.sys.socket.msghdr)* mhdr) pure nothrow @nogcCMSG_FIRSTHDR(&(local variable) core.sys.posix.sys.socket.msghdr sendMsgsendMsg);
(local variable) core.sys.posix.sys.socket.cmsghdr* cmcm.(field) int core.sys.posix.sys.socket.cmsghdr.cmsg_levelcmsg_level = (enum value) core.sys.posix.sys.socket.SOL_SOCKET = 1SOL_SOCKET;
(local variable) core.sys.posix.sys.socket.cmsghdr* cmcm.(field) int core.sys.posix.sys.socket.cmsghdr.cmsg_typecmsg_type = (enum value) core.sys.posix.sys.socket.SCM_RIGHTS = 1uSCM_RIGHTS;
(local variable) core.sys.posix.sys.socket.cmsghdr* cmcm.(field) ulong core.sys.posix.sys.socket.cmsghdr.cmsg_lencmsg_len = ulong core.sys.posix.sys.socket.CMSG_LEN!()(ulong len) pure nothrow @nogc @safeCMSG_LEN(int.(constant) ulong int.sizeof = 4LUsizeof);
*(cast(int*) inout(ubyte)* core.sys.posix.sys.socket.CMSG_DATA(return scope inout(core.sys.posix.sys.socket.cmsghdr)* cmsg) pure nothrow @nogcCMSG_DATA((local variable) core.sys.posix.sys.socket.cmsghdr* cmcm)) = (local variable) const(int) srcFdsrcFd;
(struct) core.sys.posix.sys.socket.msghdrmsghdr (local variable) core.sys.posix.sys.socket.msghdr recvMsgrecvMsg;
(local variable) core.sys.posix.sys.socket.msghdr recvMsgrecvMsg.(field) core.sys.posix.sys.uio.iovec* core.sys.posix.sys.socket.msghdr.msg_iovmsg_iov = &(local variable) core.sys.posix.sys.uio.iovec recvIovrecvIov;
(local variable) core.sys.posix.sys.socket.msghdr recvMsgrecvMsg.(field) ulong core.sys.posix.sys.socket.msghdr.msg_iovlenmsg_iovlen = 1;
(local variable) core.sys.posix.sys.socket.msghdr recvMsgrecvMsg.(field) void* core.sys.posix.sys.socket.msghdr.msg_controlmsg_control = (local variable) ubyte[24] recvControlrecvControl.(constant) ubyte* ubyte[24].ptr = &recvControlptr;
(local variable) core.sys.posix.sys.socket.msghdr recvMsgrecvMsg.(field) ulong core.sys.posix.sys.socket.msghdr.msg_controllenmsg_controllen = (constant) ulong io_uring_sendmsg_recvmsg.main.controlLen = 24LUcontrolLen;
// ---- Queue RECVMSG (user_data 0) then SENDMSG (user_data 1) ------------
// Posting the receive first guarantees there is a reader waiting; both
// complete asynchronously off the same submission.
(local variable) during.Uring ioio.putWith!((ref SubmissionEntry e, int fd, ref msghdr m) {
e.prepRecvMsg(fd, m);
e.user_data = 0;
})(during.Uring during.Uring.putWith!(function (ref during.io_uring.SubmissionEntry e, int fd, ref core.sys.posix.sys.socket.msghdr m) nothrow @nogc @safe
{
prepRecvMsg(e, fd, m, MsgFlags.NONE);
e.user_data = 0LU;
}
, int, core.sys.posix.sys.socket.msghdr)(ref int __param_0, ref core.sys.posix.sys.socket.msghdr __param_1) nothrow @nogc return ref @safeAdds new entry to the SubmissionQueue.
Note that this just adds entry to the queue and doesn't advance the tail
marker kernel sees. For that finishSq() is needed to be called next.
Also note that to actually enter new entries to kernel,
it's needed to call submit().
sock[0], (local variable) core.sys.posix.sys.socket.msghdr recvMsgrecvMsg);
(local variable) during.Uring ioio.putWith!((ref SubmissionEntry e, int fd, ref msghdr m) {
e.prepSendMsg(fd, m);
e.user_data = 1;
})(during.Uring during.Uring.putWith!(function (ref during.io_uring.SubmissionEntry e, int fd, ref core.sys.posix.sys.socket.msghdr m) nothrow @nogc @safe
{
prepSendMsg(e, fd, m, MsgFlags.NONE);
e.user_data = 1LU;
}
, int, core.sys.posix.sys.socket.msghdr)(ref int __param_0, ref core.sys.posix.sys.socket.msghdr __param_1) nothrow @nogc return ref @safeAdds new entry to the SubmissionQueue.
Note that this just adds entry to the queue and doesn't advance the tail
marker kernel sees. For that finishSq() is needed to be called next.
Also note that to actually enter new entries to kernel,
it's needed to call submit().
sock[1], (local variable) core.sys.posix.sys.socket.msghdr sendMsgsendMsg);
const (local variable) const(int) submittedsubmitted = (local variable) during.Uring ioio.int during.Uring.submit(uint want) nothrow @nogc @safeSubmits qued SubmissionEntry to be processed by kernel.
submit(2);
if ((local variable) const(int) submittedsubmitted < 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("submit failed: errno %d", -(local variable) const(int) submittedsubmitted);
return 1;
}
// ---- Reap both completions; capture the received fd from the recv CQE --
int (local variable) int recvResrecvRes = int.(constant) int int.min = -2147483648min;
int (local variable) int sendRessendRes = int.(constant) int int.min = -2147483648min;
foreach ((local variable) int __; 0 .. 2)
{
(local variable) during.Uring ioio.int during.Uring.wait(uint want = 1u) nothrow @nogcSimmilar to submit but with this method we just wait for required number
of CompletionEntries.
wait(1);
const (local variable) const(ulong) udud = (local variable) during.Uring ioio.during.io_uring.CompletionEntry during.Uring.front() pure nothrow @nogc return ref @safeGet first CompletionEntry from cq ring
front.(field) ulong during.io_uring.CompletionEntry.user_datasqe->data submission passed back
user_data;
const (local variable) const(int) resres = (local variable) during.Uring ioio.during.io_uring.CompletionEntry during.Uring.front() pure nothrow @nogc return ref @safeGet first CompletionEntry from cq ring
front.(field) int during.io_uring.CompletionEntry.resresult code for this event
res;
(local variable) during.Uring ioio.void during.Uring.popFront() pure nothrow @nogc @safeMove to next CompletionEntry
popFront();
if ((local variable) const(ulong) udud == 0) (local variable) int recvResrecvRes = (local variable) const(int) resres;
else (local variable) int sendRessendRes = (local variable) const(int) resres;
}
// Either op returning -EINVAL/-EOPNOTSUPP/-ENOSYS means this kernel lacks
// SEND/RECVMSG support — that is an expected SKIP, not a failure.
foreach ((parameter) int resres; [(local variable) int recvResrecvRes, (local variable) int sendRessendRes])
{
if ((local variable) int resres == -(constant) int core.stdc.errno.EINVAL = 22EINVAL || (local variable) int resres == -(constant) int core.stdc.errno.EOPNOTSUPP = 95EOPNOTSUPP || (local variable) int resres == -(constant) int core.stdc.errno.ENOSYS = 38ENOSYS)
{
void std.stdio.writefln!(char, int)(in char[] fmt, int __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("SKIP: kernel rejected SEND/RECVMSG (errno %d) — unsupported here", -(local variable) int resres);
return 0;
}
}
if ((local variable) int sendRessendRes < 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("SENDMSG completed with error: errno %d", -(local variable) int sendRessendRes);
return 1;
}
if ((local variable) int recvResrecvRes < 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("RECVMSG completed with error: errno %d", -(local variable) int recvResrecvRes);
return 1;
}
// ---- Extract the passed fd from the received control message -----------
(struct) core.sys.posix.sys.socket.cmsghdrcmsghdr* (local variable) core.sys.posix.sys.socket.cmsghdr* rcmrcm = inout(core.sys.posix.sys.socket.cmsghdr)* core.sys.posix.sys.socket.CMSG_FIRSTHDR(inout(core.sys.posix.sys.socket.msghdr)* mhdr) pure nothrow @nogcCMSG_FIRSTHDR(&(local variable) core.sys.posix.sys.socket.msghdr recvMsgrecvMsg);
if ((local variable) core.sys.posix.sys.socket.cmsghdr* rcmrcm is null || (local variable) core.sys.posix.sys.socket.cmsghdr* rcmrcm.(field) int core.sys.posix.sys.socket.cmsghdr.cmsg_levelcmsg_level != (enum value) core.sys.posix.sys.socket.SOL_SOCKET = 1SOL_SOCKET || (local variable) core.sys.posix.sys.socket.cmsghdr* rcmrcm.(field) int core.sys.posix.sys.socket.cmsghdr.cmsg_typecmsg_type != (enum value) core.sys.posix.sys.socket.SCM_RIGHTS = 1uSCM_RIGHTS)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("no SCM_RIGHTS control message received");
return 1;
}
const int (local variable) const(int) passedFdpassedFd = *(cast(int*) inout(ubyte)* core.sys.posix.sys.socket.CMSG_DATA(return scope inout(core.sys.posix.sys.socket.cmsghdr)* cmsg) pure nothrow @nogcCMSG_DATA((local variable) core.sys.posix.sys.socket.cmsghdr* rcmrcm));
if ((local variable) const(int) passedFdpassedFd < 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("invalid received fd %d", (local variable) const(int) passedFdpassedFd);
return 1;
}
scope (exit) int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) const(int) passedFdpassedFd);
// The clincher: the received fd is a brand-new descriptor number, yet it
// refers to the same open file. Matching device + inode proves it.
(struct) core.sys.posix.sys.stat.stat_tstat_t (local variable) core.sys.posix.sys.stat.stat_t passedStatpassedStat;
if (fstat(int core.sys.posix.sys.stat.fstat64(int, core.sys.posix.sys.stat.stat_t*) nothrow @nogc @trustedpassedFd, &(local variable) core.sys.posix.sys.stat.stat_t passedStatpassedStat) != 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("fstat(passedFd) failed");
return 1;
}
if ((local variable) core.sys.posix.sys.stat.stat_t passedStatpassedStat.(field) ulong core.sys.posix.sys.stat.stat_t.st_devst_dev != (local variable) core.sys.posix.sys.stat.stat_t srcStatsrcStat.(field) ulong core.sys.posix.sys.stat.stat_t.st_devst_dev || (local variable) core.sys.posix.sys.stat.stat_t passedStatpassedStat.(field) ulong core.sys.posix.sys.stat.stat_t.st_inost_ino != (local variable) core.sys.posix.sys.stat.stat_t srcStatsrcStat.(field) ulong core.sys.posix.sys.stat.stat_t.st_inost_ino)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("received fd refers to a different file (dev/ino mismatch)");
return 1;
}
if ((local variable) const(int) passedFdpassedFd == (local variable) const(int) srcFdsrcFd)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("received fd should be a distinct descriptor number, got the same one");
return 1;
}
void std.stdio.writefln!(char, const(int), const(int), ulong, ulong)(in char[] fmt, const(int) __param_1, const(int) __param_2, ulong __param_3, ulong __param_4) @safeEquivalent to writef(fmt, args, '\n').
writefln(
"ok: SCM_RIGHTS fd-passing via io_uring SENDMSG/RECVMSG — sent fd %d, received fd %d, same file (dev=%d ino=%d)",
(local variable) const(int) srcFdsrcFd, (local variable) const(int) passedFdpassedFd, (local variable) core.sys.posix.sys.stat.stat_t passedStatpassedStat.(field) ulong core.sys.posix.sys.stat.stat_t.st_devst_dev, (local variable) core.sys.posix.sys.stat.stat_t passedStatpassedStat.(field) ulong core.sys.posix.sys.stat.stat_t.st_inost_ino);
return 0;
}
// `0o600`-style octal literal helper (D dropped the `0o`/`0NNN` syntax).
private template (template) io_uring_sendmsg_recvmsg.octal(string s)octal(string s)
{
enum uint (constant) uint io_uring_sendmsg_recvmsg.octal!"600" = 384uoctal = {
uint (local variable) uint vv = 0;
foreach ((parameter) immutable(char) cc; (constant) string io_uring_sendmsg_recvmsg.s = "600"s) (local variable) uint vv = (local variable) uint vv * 8 + ((local variable) immutable(char) cc - '0');
return (local variable) uint vv;
}();
}