Filehandles, files, directories
ioctl#
Perform a device-control ioctl(2) system call on a filehandle.
ioctl is the direct Perl binding for the POSIX ioctl(2) syscall —
the catch-all kernel interface for device-specific operations that do
not fit the regular read/write model: terminal window size, serial
line parameters, socket device queries, tape positioning, and
anything else a driver chooses to expose through a request code. It
dispatches FUNCTION against FILEHANDLE, passing SCALAR as the
third argument — either a small integer or a pointer to a byte buffer,
depending on what the specific FUNCTION expects.
Unlike fcntl, whose request codes live in the
Fcntl module, ioctl request codes are almost
never exported as ready-made constants. The usual path is:
require "sys/ioctl.ph";
which pulls in the translated <sys/ioctl.h> header. If that file
does not exist, or does not define the code you need, build it from
the C headers with the h2ph tool shipped with Perl, or define the
constant yourself from the kernel header.
Synopsis#
require "sys/ioctl.ph";
ioctl FILEHANDLE, FUNCTION, SCALAR
ioctl($fh, $request, $buf)
my $rv = ioctl($fh, $request, $arg) || -1;
What you get back#
On success, the integer the kernel returned. When that integer is
0, Perl substitutes the dual-valued string "0 but true" — true
in boolean context, numeric 0 in numeric context, and exempt from
the Argument "..." isn't numeric warning. This means a plain
boolean test reliably distinguishes success from failure even for
requests whose success value is literally zero:
ioctl($fh, $request, $buf) or die "ioctl: $!";
On failure, ioctl returns undef and sets $!
to the relevant errno. The full mapping:
OS returns |
Perl returns |
|---|---|
|
|
|
string |
any other value |
that integer |
When you need the raw kernel return — for request codes that encode
information in the return value itself — use the idiom from
perlfunc:
my $retval = ioctl($fh, $request, $arg) || -1;
printf "System returned %d\n", $retval;
How SCALAR is used#
SCALAR is handed to the kernel as the third argument of the C
ioctl call, and its role depends on the request code:
Pointer to a buffer. The usual case. Most
ioctlrequests read from or write into a C struct whose layout is defined by the driver. Build the buffer withpackbefore the call and, for requests that write back, decode withunpackafterward. Pre-size the scalar to at least the struct’s length — Perl will grow an undersized scalar automatically, but pre-sizing makes the intent obvious and avoids surprises if the driver writes more than you expected.Small integer. A minority of requests take a bare integer rather than a pointer. If
SCALARhas no string value but does have a numeric value, Perl passes the number instead of a pointer. To guarantee this dispatch — even for a scalar that might have been stringified earlier — add0to it first:my $n = 0 + $n; # force numeric representation ioctl($fh, $request, $n);
Ignored. For request codes that take no third argument, pass
0as a placeholder. The kernel ignores it; the convention keeps the call readable.
Global state it touches#
$!— set on failure to theerrnoreturned byioctl(2).Device or descriptor state on
FILEHANDLE—ioctltypically mutates kernel state associated with the underlying file descriptor or its device (terminal attributes, socket options, tape position). That state is not Perl-visible through any other variable; read it back with anotherioctlcall using the matchingTIOCG.../SIOCG...request.
Examples#
Query the terminal window size on Linux. TIOCGWINSZ writes a
struct winsize (two unsigned short rows/cols plus two ignored
fields) into the buffer:
require "sys/ioctl.ph";
my $winsize = "\0" x 8;
ioctl(STDOUT, TIOCGWINSZ(), $winsize)
or die "TIOCGWINSZ: $!";
my ($rows, $cols) = unpack('S S', $winsize);
print "terminal is $rows rows by $cols cols\n";
Count bytes available for reading on a socket without consuming
them (FIONREAD is widely supported across Unixes):
require "sys/ioctl.ph";
my $pending = pack('L', 0);
ioctl($sock, FIONREAD(), $pending)
or die "FIONREAD: $!";
my $n = unpack('L', $pending);
print "$n bytes pending\n";
Put a socket into non-blocking mode through the FIONBIO request —
an alternative to the fcntl / F_SETFL approach, and the
form historically portable to some older systems:
require "sys/ioctl.ph";
my $on = pack('L', 1);
ioctl($sock, FIONBIO(), $on)
or die "FIONBIO: $!";
Distinguishing “syscall returned 0” from “syscall failed” using the
"0 but true" convention:
my $rv = ioctl($fh, $request, $buf);
if (!defined $rv) {
die "ioctl failed: $!";
} elsif ($rv eq "0 but true") {
# kernel returned 0 — success with a zero value
} else {
# kernel returned $rv
}
Rolling your own request code when sys/ioctl.ph is missing or
incomplete. The encoding (_IOR / _IOW / _IOWR) is kernel-
specific; on Linux the macro expands to a 32-bit value that you can
precompute and hardcode:
use constant TIOCGWINSZ => 0x5413; # Linux <asm-generic/ioctls.h>
my $winsize = "\0" x 8;
ioctl(STDOUT, TIOCGWINSZ, $winsize) or die $!;
Edge cases#
Forgot the
.phrequire: constants likeTIOCGWINSZparse as barewords with no defined value, triggering anEINVALfrom the kernel. Eitherrequire "sys/ioctl.ph"or define the constant yourself withuse constant."0 but true"return: do not compare the return with== 0as a failure test. Use a plain boolean (or die) ordefined. Comparing for equality to the literal string also works ($rv eq "0 but true") but is rarely necessary.Scalar/integer dispatch surprise: a scalar that was numeric but got stringified — for instance via interpolation into a message — flips from integer-argument to buffer-pointer semantics on the next call. Force numeric representation with
0 + $xwhen the request code expects an integer.Struct layout is platform-specific.
struct winsize,struct termios,struct ifreq, and friends differ between kernels and even between architectures on the same kernel. Do not hardcodepacktemplates from memory; verify against the target system’s header.Undersized output buffer. If the driver writes more bytes than
SCALARcurrently holds, the scalar is grown, but you have no hint in Perl that this happened. Pre-size to at least the struct’s length with"\0" x Nso that reads past the end of the buffer are driver bugs, not Perl bugs.Not every system implements every request.
ioctlitself is POSIX, but specific request codes are driver-defined. An unsupported request fails withENOTTYorEINVAL; anioctlcall on a system withoutioctl(2)at all raises an exception.ioctlvsfcntl: identical argument-processing and return-value convention, different kernel interface. Usefcntlfor descriptor flags and POSIX locks; useioctlfor device control codes.
Differences from upstream#
Fully compatible with upstream Perl 5.42.
See also#
fcntl— same calling and return-value convention; used for descriptor-level flags and advisory locks rather than device-specific control codespack/unpack— required for building and decoding the struct buffers mostioctlrequests exchange with the kernelopen/sysopen— open the device or special file whose driver theioctlrequest will talk tobinmode— someioctlrequests (e.g. terminal mode changes) interact with PerlIO buffering; switch to raw mode when the driver and PerlIO disagree about line disciplineselect/sysread— the normal I/O path; reach forioctlonly when the operation you need is not expressible as a read or write