--- name: sysopen signatures: - 'sysopen FILEHANDLE, FILENAME, MODE' - 'sysopen FILEHANDLE, FILENAME, MODE, PERMS' since: 5.002 status: documented categories: ["Filehandles, files, directories"] --- ```{index} single: sysopen; Perl built-in ``` *[Filehandles, files, directories](../perlfunc-by-category)* # sysopen Open a file the low-level way, passing an integer `MODE` bitmask straight through to the underlying [`open(2)`](../../Fcntl) system call. `sysopen` is the escape hatch for when [`open`](open)'s string-mode DWIM is in your way — when you need `O_EXCL` for an atomic create-if-not-exist, `O_NOFOLLOW` to refuse symlinks, `O_NONBLOCK` on a FIFO, or any other flag that has no corresponding `"<"` / `">"` / `">>"` string. The flag constants live in [`Fcntl`](../../Fcntl) and are combined with `|`. ## Synopsis ```perl sysopen FILEHANDLE, FILENAME, MODE sysopen FILEHANDLE, FILENAME, MODE, PERMS ``` ## What you get back `1` on success, [`undef`](undef) on failure (with [`$!`](../perlvar) set to the `errno` from the failed syscall). Unlike [`open`](open), there is no magic — `FILENAME` is taken literally, no leading `>`, `<`, `|`, or `-` is interpreted, no layered mode string is parsed. If `FILEHANDLE` is an undefined scalar, it is autovivified into a fresh handle: ```perl sysopen my $fh, $path, O_RDONLY or die "sysopen $path: $!"; ``` ## MODE — the Fcntl bitmask `MODE` is an integer built by OR-ing constants from [`Fcntl`](../../Fcntl). The access mode is mandatory and mutually exclusive; the modifier flags are optional and combined freely: | Flag | Meaning | |---------------|---------------------------------------------------------| | `O_RDONLY` | Open for reading only. | | `O_WRONLY` | Open for writing only. | | `O_RDWR` | Open for reading and writing. | | `O_CREAT` | Create the file if it does not exist. | | `O_EXCL` | With `O_CREAT`: fail if the file already exists. | | `O_APPEND` | Every write seeks to end-of-file first (atomic append). | | `O_TRUNC` | Truncate the file to zero length on open. | | `O_NONBLOCK` | Non-blocking mode for the resulting descriptor. | | `O_NOFOLLOW` | Fail with `ELOOP` if the final path component is a symlink. | Import them by name or with a tag: ```perl use Fcntl qw(O_RDWR O_CREAT O_EXCL O_APPEND); # or use Fcntl ':DEFAULT'; ``` The legacy values `0`, `1`, `2` for read-only, write-only, read-write also work on every system Perl supports, but name the constants — the resulting code survives cross-platform review and reads without a glossary. ## PERMS — the create-time permission bits `PERMS` is an octal mode (like [`chmod`](chmod) takes) applied to the inode **only when `O_CREAT` actually creates the file**. If `PERMS` is omitted, Perl passes `0666`. The process [`umask`](umask) is applied by the kernel on top, so a default `umask` of `022` yields `0644` on disk. ```perl sysopen my $fh, $path, O_WRONLY | O_CREAT | O_EXCL, 0600 or die "create $path: $!"; ``` Do not write `0644` as the `PERMS` argument without a reason. A hard-coded `0644` strips group-write even from users who deliberately set a permissive `umask`; `0666` plus the user's `umask` is the portable default and is what `sysopen` uses when you omit the argument entirely. ## Global state it touches - [`$!`](../perlvar) — set on failure to the `errno` returned by [`open(2)`](../../Fcntl). - [`${^OPEN}`](../perlvar) — the `open` pragma's layer configuration. `sysopen` applies the **same** default PerlIO stack [`open`](open) would apply for a layer-less call. Use [`binmode`](binmode) immediately after a successful `sysopen` when you need to override or strip layers (typical for binary files, sockets-as-files, or `O_NONBLOCK` descriptors). - Process [`umask`](umask) — masks `PERMS` when `O_CREAT` creates the file. ## Examples Atomic create-if-not-exist — the canonical reason to reach for `sysopen`: ```perl use Fcntl qw(O_WRONLY O_CREAT O_EXCL); sysopen my $fh, "lock.pid", O_WRONLY | O_CREAT | O_EXCL, 0644 or die "another instance is already running: $!"; print $fh "$$\n"; close $fh; ``` Open-or-create for read/write, preserving existing contents: ```perl use Fcntl qw(O_RDWR O_CREAT); sysopen my $fh, $path, O_RDWR | O_CREAT, 0666 or die "sysopen $path: $!"; ``` Append-only log with the append guarantee — every write lands at EOF regardless of interleaving with other writers: ```perl use Fcntl qw(O_WRONLY O_CREAT O_APPEND); sysopen my $log, "/var/log/app.log", O_WRONLY | O_CREAT | O_APPEND, 0644 or die "open log: $!"; print $log "startup\n"; ``` Open a FIFO without blocking on the reader-side handshake: ```perl use Fcntl qw(O_RDONLY O_NONBLOCK); sysopen my $fifo, "/tmp/q", O_RDONLY | O_NONBLOCK or die "sysopen fifo: $!"; ``` Refuse to follow a symlink at the leaf — useful in directories writable by untrusted users: ```perl use Fcntl qw(O_RDONLY O_NOFOLLOW); sysopen my $fh, "$dir/config", O_RDONLY | O_NOFOLLOW or die "sysopen config: $!"; ``` Force byte-level I/O after opening — strip any `:utf8` or `:crlf` layer the default PerlIO stack would otherwise install: ```perl sysopen my $fh, $path, O_RDONLY or die $!; binmode $fh; ``` ## Edge cases - **`O_EXCL` without `O_CREAT` is a no-op.** The exclusivity check only triggers when `O_CREAT` asks the kernel to create the file. Always write them together: `O_CREAT | O_EXCL`. - **`O_EXCL` is not a lock.** It prevents a successful open on an already-existing file, nothing more. Once the file exists, every subsequent `sysopen` with `O_CREAT | O_EXCL` fails. It does not serialize access to the contents — use [`flock`](flock) for that. - **`O_EXCL` on network filesystems.** NFS implementations vary; older NFS versions silently lose the exclusive semantics. Do not rely on `O_EXCL` across NFSv2. - **`O_CREAT | O_EXCL` and symlinks.** With both flags set, the kernel refuses to open a pre-existing symlink at the final path component. It does **not** protect intermediate path components; use `O_NOFOLLOW` or resolve the directory with [`opendir`](opendir) plus `openat` semantics if you need that. - **`O_TRUNC` with `O_RDONLY`.** Behavior is undefined by POSIX. Do not combine them. - **Omitted `PERMS`.** The default is `0666`, masked by `umask`. It is **not** `0644`. Omit `PERMS` unless you actively want to override the user's `umask`. - **`FILEHANDLE` as expression.** If `FILEHANDLE` is a bareword, it names a package global. If it is an expression evaluating to a glob, globref, or `IO::Handle`, that handle is opened. If it is an undefined scalar, a fresh handle is autovivified into it — the lexical form used throughout these examples. - **No magic on `FILENAME`.** A `FILENAME` of `"-"` opens a file literally named `-`, not `STDIN`. A leading `>` opens a file literally named `>filename`, not a write to `filename`. This is the entire point of `sysopen` versus [`open`](open). - **Default PerlIO layers still apply.** `sysopen` is "low-level" relative to the mode string, not relative to PerlIO. The resulting handle has the same layer stack as a layer-less [`open`](open); call [`binmode`](binmode) to change it. - **Closed or invalid `FILEHANDLE` expression.** Returns [`undef`](undef) with `$!` set (typically `EBADF` for a bad existing descriptor; `ENOENT`, `EACCES`, `EEXIST`, `ELOOP` for the path-level errors). ## Differences from upstream Fully compatible with upstream Perl 5.42. ## See also - [`open`](open) — higher-level opener with string-mode parsing, pipes, `-` as `STDIN` / `STDOUT`, and layered modes; use it unless you specifically need a flag `sysopen` exposes - [`fcntl`](fcntl) — change flags on an already-open descriptor (e.g. set `O_NONBLOCK` after the fact); shares the `Fcntl` constant vocabulary - [`sysread`](sysread) — unbuffered read; works on any handle, not just one obtained from `sysopen`, despite the name - [`syswrite`](syswrite) — unbuffered write, same note - [`Fcntl`](../../Fcntl) — the module that exports `O_*` constants and the `:DEFAULT` / `:flock` / `:mode` tags - [`binmode`](binmode) — set or strip PerlIO layers on the handle after opening - [`umask`](umask) — the process mask that subtracts bits from `PERMS` when `O_CREAT` creates the file