package Sys::GetRandom;
use strict;
use warnings;

use Exporter qw(import);
use Carp qw(croak);

use XSLoader;
BEGIN {
    our $VERSION = '0.02';
    XSLoader::load __PACKAGE__, $VERSION;
}

our @EXPORT_OK = qw(
    GRND_RANDOM
    GRND_NONBLOCK
    getrandom
    random_bytes
);

sub random_bytes {
    my ($n) = @_;
    $n |= 0;
    $n >= 0 && $n <= 256
        or croak "Argument to random_bytes() must be an integer between 0 and 256, not $n";
    return '' if $n == 0;
    defined(my $r = getrandom(my $buf, $n))
        or die "Internal error: getrandom(\$buf, $n) failed: $!";
    $r == $n
        or die "Internal error: getrandom(\$buf, $n) returned $r";
    $buf
}

1
__END__

=head1 NAME

Sys::GetRandom - Perl interface to getrandom(2)

=head1 SYNOPSIS

=for highlighter language=perl

    use Sys::GetRandom qw(getrandom random_bytes GRND_NONBLOCK GRND_RANDOM);
    my $n = getrandom($buf, $count, $flags, $offset);
    my $bytes = random_bytes($count);

=head1 DESCRIPTION

This module provides a Perl interface to the L<getrandom(2)> call present on
Linux and FreeBSD. It exports (on request) two functions and two constants.

=head2 Functions

=over

=item getrandom SCALAR, LENGTH

=item getrandom SCALAR, LENGTH, FLAGS

=item getrandom SCALAR, LENGTH, FLAGS, OFFSET

Generates up to I<LENGTH> bytes of random data and stores them in I<SCALAR>.
Returns the number of random bytes generated, or C<undef> on error (in which
case C<$!> is also set).

By default, C<getrandom> is equivalent to reading from F</dev/urandom> (but
without accessing the file system or requiring the use of a file descriptor).
If F</dev/urandom> has not been initialized yet, C<getrandom> will block by
default.

If F</dev/urandom> has been initialized and I<LENGTH> is 256 or less,
C<getrandom> will atomically return the requested amount of random data (i.e.
it will generate exactly I<LENGTH> bytes of data and will not be interrupted by
a signal). For larger values of I<LENGTH> it may be interrupted by signals and
either generate fewer random bytes than requested or fail with C<$!> set to
C<EINTR>.

The I<FLAGS> argument must be either 0 (the default value) or the bitwise OR of
one or more of the following flags:

=over

=item C<GRND_RANDOM>

Read from the same source as F</dev/random>, not F</dev/urandom> (the default).

=item C<GRND_NONBLOCK>

By default, C<getrandom> will block if F</dev/urandom> has not been initialized
yet or (with C<GRND_RANDOM>) if there are no random bytes available in
F</dev/random>. If the C<GRND_NONBLOCK> flag is passed, it will fail
immediately instead, returning C<undef> and setting C<$!> to C<EAGAIN>.

=back

By default, the generated bytes are placed at the beginning of I<SCALAR>. You
can pass a positive value for I<OFFSET> to place them within the string,
leaving the first I<OFFSET> bytes unchanged.

=item random_bytes LENGTH

Generates and returns a string of I<LENGTH> random bytes.

I<LENGTH> must be between 0 and 256 (inclusive).

This is just a wrapper around C<getrandom> with default flags.

=back

=head1 AUTHOR

Lukas Mai, C<< <lmai at web.de> >>

=head1 COPYRIGHT & LICENSE

Copyright 2023 Lukas Mai.

This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.

See L<https://dev.perl.org/licenses/> for more information.

=head1 SEE ALSO

L<Crypt::SysRandom::XS>, L<Sys::GetRandom::PP>