=head1 NAME

Fugu::Control, Fugu::Control::Client - a control socket for a running daemon

=head1 SYNOPSIS

    # In the daemon
    use Fugu::Control;

    my $control = Fugu::Control->new(path => '/var/run/myd/control.sock');
    $control->register(status  => sub ($args) { { paired => 1 } });
    $control->register(devices => sub ($args) { [ @devices ] });
    $control->listen(loop => $loop) or die $control->error;

    # and at shutdown
    $control->shutdown(loop => $loop);

    # In the tool
    my $client = Fugu::Control::Client->new(
        path => '/var/run/myd/control.sock');

    my $status = $client->request('status');
    unless ($status) {
    	die "the daemon is not running\n" if $client->socket_absent;
    	die $client->error . "\n";
    }

=head1 DESCRIPTION

A tool that reports on a daemon has two ways to get its answer.
It can guess from files on disk, or it can ask the daemon.
The first way reads what the daemon wrote at some earlier time, and
it is wrong whenever the two disagree.
B<Fugu::Control> is the second way.

The server registers with a L<Fugu::EventLoop> and answers commands
from process state.
The client connects, sends one request, and reads the reply.
The payload is JSON, and the transport is the imsg(3) framing of
L<Fugu::Imsg>.

Everything that arrives on the socket is untrusted.
An unknown command, a payload that is not JSON, and a frame over the
limit all give an error reply.
None of them ends the daemon.

=head2 The server

=over 4

=item C<new(%args)>

Make a server.
The method opens nothing.

C<path> is the socket, and it is necessary.
C<log> is the logger, and it defaults to the process default of
L<Fugu::Log>.

=item C<register($command, $code)>

Add a command.
The code gets the decoded arguments hash reference and returns the
reply, which must encode as JSON.
A handler that dies gives the caller an error reply.

=item C<listen(%args)>

Bind the socket and start accepting.
The listener and every accepted connection register with the event
loop that C<loop> names.
That argument is necessary, and the method dies without it.

C<mode> is the socket mode, as an integer, with the default C<0600>.
C<group> is the socket group, as a name or as a numeric group id,
with no default.
A group name resolves with C<getgrnam>.
An unresolvable name, a failed chown(2) and a failed chmod(2) are
each a recoverable failure: the method takes the socket down, sets
C<error()>, and returns C<undef>, so a half-built socket never
accepts a connection.

The socket never accepts a wider set of users than the final set.
With no C<group> the mode holds from birth, through a umask(2) guard.
With a C<group> the socket binds under the owner bits of the final
mode, takes the group with chown(2), and widens to C<mode> with
chmod(2) last.
A daemon drops privileges first, and it calls C<listen> after.
A process can chgrp its own file to a group that it belongs to, so
the group form needs no root.

=item C<accept_one($loop)>

Take one connection and register it as a read handler on the loop.
Where C<peer_supported()> is true, the method reads the peer
credentials of the connection once, with getsockopt(2) and
C<SO_PEERCRED>.
A credential read that fails closes the connection at once, and the
server logs the reason at the error level: a control socket that
cannot name its peer must not answer.

=item C<peer()>

The credentials of the connection that the server is answering now,
as a hash reference with C<uid>, C<gid> and C<pid>: the effective
user id, the effective group id, and the process id of the peer.
A handler that needs the operator identity calls it, and a handler
that does not need it ignores it.

The method returns C<undef> outside a handler call, and C<undef>
where C<peer_supported()> is false.
It holds no policy and reports three numbers.
The group of the socket is the coarse gate, and the handler is the
fine gate.

On OpenBSD the read returns a C<struct sockpeercred>, which holds the
user id, then the group id, then the process id.
The field order differs from the Linux C<struct ucred>, which holds
the process id first, and one module must not carry two field orders.
The method therefore reads the credentials on OpenBSD only, and it
reports "not supported" everywhere else, through C<peer_supported()>.

=item C<peer_supported()>

A class method.
It returns true only where the platform reports peer credentials in
the C<struct sockpeercred> order.
A caller or a test uses it to tell "not supported" from "the read
failed".

=item C<shutdown(%args)>

Close every connection, close the listener, and remove the socket.
A socket left behind names a daemon that is not there.

=item C<path()>

The socket path.

=item C<error()>

The most recent failure.

=back

=head2 The client

=over 4

=item C<new(%args)>

Make a client.
C<path> is necessary; C<timeout> is the per-frame deadline and
defaults to 5 seconds.

=item C<request($command, $args)>

Send one command and return the decoded reply, or C<undef> with the
reason in C<error()>.
The method connects if it is not connected.

=item C<connect()>, C<disconnect()>

Open and close the connection by hand.
Both are idempotent.

=item C<socket_absent()>

Report if the most recent failure was an absent socket, and not a
refusal.
A tool says "the daemon is not running" for the first and "the daemon
said no" for the second.
A tool that cannot tell them apart reports the wrong thing to an
operator half the time.

A socket inside a directory that the caller may not search is not an
absent socket.
C<connect()> tells the two apart and reports the permission, because
an operator who reads "not running" about a daemon that is running
looks in the wrong place.

=item C<error()>

The most recent failure.

=back

=head1 RETURN VALUES

C<listen()> returns the server object, or C<undef> with the reason in
C<error()>.
C<request()> returns the decoded reply, or C<undef>.
C<socket_absent()> and C<peer_supported()> return 1 or 0.
C<peer()> returns a hash reference, or C<undef>.
Every other method returns its object.

=head1 FILES

The socket that the caller names.
The server creates it mode 0600 by default, under a umask(2) guard,
so it is never world-reachable even for an instant.
The C<mode> and C<group> arguments of C<listen()> widen it, and the
socket still never accepts a wider set of users than the final set.

The mode of the socket is the inner boundary.
The directory that holds it is the outer one, and the caller owns
that.
A daemon that drops privileges creates the directory while it is
still root, mode 0700, owned by the daemon user.

=head1 ERRORS

C<new()> dies without a path, and C<register()> dies without a code
reference.
C<listen()> dies for a C<mode> that is not an integer from 0 to
C<0777>.
Those are programming errors.

Nothing that arrives on the socket makes anything die.
A malformed request, an unknown command, and a handler that died all
give an error reply, and the connection stays usable.

C<listen()> refuses to take a socket that another process answers on,
and replaces one that nothing is behind.
bind(2) fails on an existing name, and a daemon that will not start
after a crash needs a hand at every reboot.

=head1 SEE ALSO

imsg(3), L<Fugu::EventLoop>, L<Fugu::Imsg>, L<Fugu::Log>

=head1 AUTHORS

Dick Olsson E<lt>hi@senzilla.ioE<gt>

=head1 CAVEATS

The socket carries no secret.
That is a rule for the caller, which writes the command handlers, and
this module cannot enforce it.
A daemon whose configuration holds a setup code and a broker password
must not answer a command that echoes them back: the reply lands in
the terminal of an operator who may not be alone.

One imsg frame carries about 16 KB.
A larger reply spans several frames and the client puts them back
together, up to one megabyte in total.
A command that would answer with more than that gets an error
instead.

A handler runs inside the event loop of the daemon.
A handler that blocks blocks the daemon.

=cut
