--- name: ioctl signature: 'ioctl FILEHANDLE, FUNCTION, SCALAR' status: documented categories: ["Filehandles, files, directories"] --- ```{index} single: ioctl; Perl built-in ``` *[Filehandles, files, directories](../perlfunc-by-category)* # 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`](fcntl), whose request codes live in the [`Fcntl`](../../Fcntl) module, `ioctl` request codes are almost never exported as ready-made constants. The usual path is: ```perl require "sys/ioctl.ph"; ``` which pulls in the translated `` 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 ```perl 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: ```perl ioctl($fh, $request, $buf) or die "ioctl: $!"; ``` On failure, `ioctl` returns [`undef`](undef) and sets [`$!`](../perlvar) to the relevant `errno`. The full mapping: | OS returns | Perl returns | |-----------------|----------------------| | `-1` | [`undef`](undef) | | `0` | string `"0 but true"` | | 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`: ```perl 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 `ioctl` requests read from or write into a C struct whose layout is defined by the driver. Build the buffer with [`pack`](pack) before the call and, for requests that write back, decode with [`unpack`](unpack) afterward. 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 `SCALAR` has 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 — add `0` to it first: ```perl my $n = 0 + $n; # force numeric representation ioctl($fh, $request, $n); ``` - **Ignored.** For request codes that take no third argument, pass `0` as a placeholder. The kernel ignores it; the convention keeps the call readable. ## Global state it touches - [`$!`](../perlvar) — set on failure to the `errno` returned by `ioctl(2)`. - Device or descriptor state on `FILEHANDLE` — `ioctl` typically 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 another `ioctl` call using the matching `TIOCG...` / `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: ```perl 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): ```perl 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`](fcntl) / `F_SETFL` approach, and the form historically portable to some older systems: ```perl 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: ```perl 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: ```perl use constant TIOCGWINSZ => 0x5413; # Linux my $winsize = "\0" x 8; ioctl(STDOUT, TIOCGWINSZ, $winsize) or die $!; ``` ## Edge cases - **Forgot the `.ph` require**: constants like `TIOCGWINSZ` parse as barewords with no defined value, triggering an `EINVAL` from the kernel. Either `require "sys/ioctl.ph"` or define the constant yourself with [`use constant`](use). - **`"0 but true"` return**: do not compare the return with `== 0` as a failure test. Use a plain boolean (`or die`) or [`defined`](defined). 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 + $x` when 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 hardcode [`pack`](pack) templates from memory; verify against the target system's header. - **Undersized output buffer.** If the driver writes more bytes than `SCALAR` currently 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 N` so that reads past the end of the buffer are driver bugs, not Perl bugs. - **Not every system implements every request.** `ioctl` itself is POSIX, but specific request codes are driver-defined. An unsupported request fails with `ENOTTY` or `EINVAL`; an `ioctl` call on a system without `ioctl(2)` at all raises an exception. - **`ioctl` vs [`fcntl`](fcntl)**: identical argument-processing and return-value convention, different kernel interface. Use [`fcntl`](fcntl) for descriptor flags and POSIX locks; use `ioctl` for device control codes. ## Differences from upstream Fully compatible with upstream Perl 5.42. ## See also - [`fcntl`](fcntl) — same calling and return-value convention; used for descriptor-level flags and advisory locks rather than device-specific control codes - [`pack`](pack) / [`unpack`](unpack) — required for building and decoding the struct buffers most `ioctl` requests exchange with the kernel - [`open`](open) / [`sysopen`](sysopen) — open the device or special file whose driver the `ioctl` request will talk to - [`binmode`](binmode) — some `ioctl` requests (e.g. terminal mode changes) interact with PerlIO buffering; switch to raw mode when the driver and PerlIO disagree about line discipline - [`select`](select) / [`sysread`](sysread) — the normal I/O path; reach for `ioctl` only when the operation you need is not expressible as a read or write