--- name: msgctl signature: 'msgctl ID, CMD, ARG' since: 5.0 status: documented categories: ["SysV IPC"] --- ```{index} single: msgctl; Perl built-in ``` *[SysV IPC](../perlfunc-by-category)* # msgctl Perform a control operation on a System V IPC message queue. `msgctl` is a direct wrapper around the C `msgctl(2)` system call. It inspects, modifies, or removes the message queue identified by `ID`. The operation selected by `CMD` determines how `ARG` is used — either as an input descriptor, an output buffer, or ignored entirely. The symbolic constants needed for `CMD` (`IPC_STAT`, `IPC_SET`, `IPC_RMID`, and on Linux `IPC_INFO`, `MSG_STAT`, `MSG_INFO`) live in `IPC::SysV`, which you have to pull in before the names resolve. ## Synopsis ```perl use IPC::SysV; msgctl $id, IPC_STAT, $buf # read queue info into $buf msgctl $id, IPC_SET, $buf # write queue info from $buf msgctl $id, IPC_RMID, 0 # destroy the queue ``` ## What you get back Return value follows the same convention as [`ioctl`](ioctl): - [`undef`](undef) on error, with [`$!`](../perlvar) set. - The string `"0 but true"` when the underlying call returned `0`. This is the zero-valued success case — it tests true in boolean context and compares equal to `0` in numeric context, so one check handles both success and failure. - The raw non-zero integer return otherwise (for example the index returned by Linux-specific `MSG_STAT`). The `"0 but true"` convention exists precisely so that `msgctl(...) or die` does the right thing even when the syscall legitimately returns zero: ```perl msgctl $id, IPC_RMID, 0 or die "msgctl IPC_RMID failed: $!"; ``` ## How `ARG` is used `ARG` plays three different roles depending on `CMD`. The kernel only touches the memory in the directions the command requires: - **`IPC_STAT`** — `ARG` must be a **writable scalar**. The kernel fills it with the packed `msqid_ds` structure describing the queue (permissions, owner, last-send/last-receive times, queue byte limits, counts). Unpack with `IPC::SysV` helpers rather than hand-rolling the layout; `msqid_ds` varies across kernels and libc versions. - **`IPC_SET`** — `ARG` must contain a packed `msqid_ds` **produced by a prior `IPC_STAT`** on the same (or compatible) queue. Only a few fields are honoured: `msg_perm.uid`, `msg_perm.gid`, `msg_perm.mode` (low 9 bits), and `msg_qbytes`. The rest is ignored. - **`IPC_RMID`** — `ARG` is unused. Pass `0`. The queue is marked for destruction; any blocked [`msgsnd`](msgsnd) / [`msgrcv`](msgrcv) on it fails with `EIDRM`. On Linux only, `MSG_STAT` and `MSG_INFO` also accept a writable scalar for `ARG` and return extra data; these are non-portable and should be gated behind a runtime check. ## Global state it touches - [`$!`](../perlvar) — set on failure with the system errno (`EPERM`, `EACCES`, `EINVAL`, `EIDRM`, `EFAULT` are the common values). - No other special variables are touched. ## Examples Read the queue header and inspect the current byte limit: ```perl use IPC::SysV qw(IPC_STAT); my $buf = ""; msgctl($id, IPC_STAT, $buf) or die "msgctl IPC_STAT failed: $!"; # Layout is platform-dependent; use IPC::SysV helpers to decode. ``` Shrink the queue's byte budget. Round-trip through `IPC_STAT` so the immutable fields keep their current kernel values: ```perl use IPC::SysV qw(IPC_STAT IPC_SET); my $buf = ""; msgctl($id, IPC_STAT, $buf) or die "stat: $!"; # ... modify msg_qbytes inside $buf via IPC::SysV ... msgctl($id, IPC_SET, $buf) or die "set: $!"; ``` Destroy a queue when done. `ARG` is ignored, but it still has to be passed — `0` is the conventional placeholder: ```perl use IPC::SysV qw(IPC_RMID); msgctl($qid, IPC_RMID, 0) or warn "could not remove queue $qid: $!"; ``` Idiomatic success test that works for the `"0 but true"` case: ```perl my $rc = msgctl($id, IPC_RMID, 0); die "msgctl failed: $!" unless defined $rc; # undef = error # $rc is now either "0 but true" or a positive integer ``` ## Edge cases - **`IPC::SysV` not loaded**: bare constant names like `IPC_STAT` become barewords and, under `use strict`, a compile-time error. Always `use IPC::SysV` first. - **Scalar passed to `IPC_STAT` is not writable**: croaks with `Modification of a read-only value attempted`. Use a plain `my $buf = ""` — not a constant, not a string literal. - **Length mismatch on `IPC_SET`**: if `ARG` is shorter than the kernel's `msqid_ds`, the call fails with `EFAULT`. Always start from an `IPC_STAT` buffer rather than packing one from scratch. - **Removed queue (`EIDRM`)**: once `IPC_RMID` has been issued, any further operation on the same ID fails with [`$!`](../perlvar) set to `EIDRM`, not `EINVAL`. Scripts that poll a queue must treat `EIDRM` as "queue is gone, stop." - **Permission errors**: `IPC_SET` and `IPC_RMID` require effective UID match or `CAP_SYS_ADMIN`. Kernel returns `EPERM`, not `EACCES`, for ownership failures. - **Return-value trap**: `if (msgctl(...) == 0)` is **wrong** — on success the value is the string `"0 but true"`, which stringifies as non-empty but compares numerically equal to `0`. Test with `defined` for the error case, or rely on `or die`. - **Platform availability**: System V IPC is not universal. On systems where the kernel omits it, `msgctl` raises the runtime exception `msgctl not implemented`. ## Differences from upstream Fully compatible with upstream Perl 5.42 on Linux. System V IPC is the only supported IPC surface; pperl targets Linux, so the portability caveats that upstream `perlport` lists for non-Linux platforms do not apply. ## See also - [`msgget`](msgget) — obtain the queue ID that `msgctl` operates on - [`msgsnd`](msgsnd) — send a message to the queue - [`msgrcv`](msgrcv) — receive a message from the queue - [`semctl`](semctl) — the semaphore-set analogue, same return convention and same `IPC_STAT` / `IPC_SET` / `IPC_RMID` pattern - [`shmctl`](shmctl) — the shared-memory-segment analogue - `IPC::SysV` — the constants (`IPC_STAT`, `IPC_SET`, `IPC_RMID`) and the `msqid_ds` pack/unpack helpers this call requires