FuguLib::State(3p) Perl Library Manual FuguLib::State(3p)

FuguLib::StatePID file handling

use FuguLib::State;

my $state = FuguLib::State->new('/var/run/mydaemon.pid');

if (my $pid = $state->is_running) {
    die "already running as pid $pid\n";
}

$state->write_pid;

FuguLib::State reads and writes a PID file and answers whether the process it names is still there. It exists so that a daemon, its rc.d(8) script, and a control utility can all agree on the same question.

($pidfile) creates a state object for the named file. The argument is positional, not a keyword pair. The file is neither created nor read at this point.

write_pid($pid) writes $pid, followed by a newline, to the PID file, truncating whatever was there. $pid defaults to the calling process. The file is locked with flock(2) between the open and the write.

read_pid() returns the process ID recorded in the file. The first line must be a run of decimal digits; anything else, including a missing file, reads as no PID at all.

remove() deletes the PID file.

is_running() returns the recorded process ID if that process is alive, as judged by FuguLib::Process(3p).

is_stale() reports whether the PID file names a process that is no longer alive: the condition under which a daemon may take the file over rather than refusing to start.

new() returns a state object, or undef if $pidfile is undef.

write_pid() returns 1 on success and 0 if the file could not be opened or locked.

read_pid() and is_running() return a process ID, or undef when there is none to report.

remove() returns 1 if the file was already absent, otherwise the result of unlink(2).

is_stale() returns 0 when the file holds no readable PID, so a missing PID file is not stale.

Refuse to start twice, and clean up after a crash:

my $state = FuguLib::State->new('/var/run/mydaemon.pid');

if (my $pid = $state->is_running) {
    die "mydaemon already running as pid $pid\n";
}
$state->remove if $state->is_stale;

FuguLib::Daemon->daemonize(
    on_fork => sub ($pid) { $state->write_pid($pid) },
);

No method dies. Failures to open, lock or unlink the file are reported through the return value, and the reason is left in $!.

flock(2), unlink(2), FuguLib::Daemon(3p), FuguLib::Process(3p)

Dick Olsson <hi@senzilla.io>

The lock taken by write_pid() is released when the file is closed, at the end of that call. It serialises two concurrent writes; it does not hold the PID file for the lifetime of the daemon, and read_pid() does not take a lock at all.

Checking is_running() and then acting on the answer is inherently racy: the process may exit, or another may claim the file, in between.

is_running() inherits the limits of FuguLib::Process(3p)'s liveness check, which cannot tell a recycled process ID from the original.

July 27, 2026 OpenBSD