App-CamelPKI

 view release on metacpan or  search on metacpan

lib/App/CamelPKI/SysV/Apache.pm  view on Meta::CPAN

        } elsif ($k eq "-certification_chain") {
            write_file($self->_ca_bundle_filename,
                       join("", map { $_->serialize } @$v));
        } else {
            throw App::CamelPKI::Error::Internal
                ("INCORRECT_ARGS",
                 -details => "Unknown named option $k");
        }
    }
}

=head2 is_operational()

Returns true if and only if the ad-hoc cryptographic material has been
added to this Web server using L</set_keys>.

=cut

# The above POD is ambiguous on purpose: ->is_operational may someday
# return true even if there is no CA chain available.
sub is_operational {
    my ($self) = @_;
    -r $self->_key_filename && -r $self->_certificate_filename &&
        -r $self->_ca_bundle_filename;
}

=head2 certificate()

Returns the Web server's SSL certificate, as an instance of
L<App::CamelPKI::Certificate>.

=cut

sub certificate {
    App::CamelPKI::Certificate->load(shift->_certificate_filename);
}

=head2 update_crl($crl)

Given $crl, an instance of L<App::CamelPKI::CRL>, verifies the signature
thereof and stores it into this Apache server if and only if it
matches one of the CAs previously installed using L</set_keys>'
C<-certificate_chain> named option, B<and> $crl is older than any CRL
previously added with I<update_crl()>.  If these security checks are
successful and Apache is already running, it will be restarted so as
to take the new CRL into account immediately.

Note that a Web server works perfectly without a CRL, and therefore
calling I<update_crl> is optional.  However, remember that CRLs have
expiration dates: once a CRL has been installed using this method, one
should plan for a suitable mechanism (e.g. a crontab entry) that will
download updated CRLs on a regular basis and submit them using
I<update_crl()>.

=cut

sub update_crl { "UNIMPLEMENTED" }

=head2 start(%opts)

Starts the daemon synchronously, meaning that I<start> will only
return control to its caller after ensuring that the Apache process
wrote its PID file and bound to its TCP port. I<start()> is
idempotent, and terminates immediately if the serveur is already up.

An L<App::CamelPKI::Error/App::CamelPKI::Error::OtherProcess> exception will be
thrown if the server doesn't answer within L</async_timeout> seconds.
An L<App::CamelPKI::Error/App::CamelPKI::Error::User> exception will be thrown
if one attempts to I<start()> the server before providing it with its
certificate and key with L</set_keys>.

Available named options are:

=over

=item I<< -strace => $strace_logfile >>

Starts Apache under the C<strace> debug command, storing all results
into $strace_logfile.

=item I<< -X => 1 >>

Starts Apache with the C<-X> option, which causes it to launch only
one worker and to not detach from the terminal.

=item I<< -gdb => 1 >>

=item I<< -gdb => $tty >>

Starts Apache under the GNU debugger attached to tty $tty (or the
current tty, if the value 1 is specified).  Incompatible with
I<-strace>.  If this option is specified, I<start()> will not time out
after L</async_timeout> seconds, but will instead wait an unlimited
amount of time for the server to come up.

=item I<< -exec => 1 >>

Don't fork a subprocess, use the C<exec> system call instead (see
L<perlfunc/exec>) to run Apache directly (or more usefully, some
combination of Apache and a debugger, according to the above named
options).  The current UNIX process will turn into Apache, and the
I<start> method will therefore never return.

=back

=cut

sub start {
    throw App::CamelPKI::Error::Internal("WRONG_NUMBER_ARGS")
        unless (@_ % 2);
    my ($self, %opts) = @_;
    throw App::CamelPKI::Error::OtherProcess("Apache is wedged")
        if ($self->is_wedged);
    return if $self->is_started;

    $self->_write_config_file();
    my (@debugprecmd, @dashX);
    my $timeout = 1;
    if (defined(my $stracefile = delete $opts{-strace})) {
        @debugprecmd = ("strace", -o => $stracefile,
                   qw(-f -s 2000));
    } elsif (my $tty = delete $opts{-gdb}) {
        @debugprecmd = ("gdb", ( ($tty eq "1") ? () : ("-tty=$tty") ),
                        "--args");
        $timeout = 0;
    }
    if (delete $opts{-X}) { @dashX = qw(-X); }

    my @fullcmdline =
        (@debugprecmd,
         $self->_apache_bin, @dashX, -f => $self->_config_filename);
    if ($opts{-exec}) {
        exec(@fullcmdline) or
            throw App::CamelPKI::Error::OtherProcess("cannot exec() Apache",
                                                -cmdline => \@fullcmdline);
    } else {
        # Double fork(), so we don't have to bother with zombies :
        fork_and_do { fork_and_do {
            exec @fullcmdline;
        } };
    }

    if ($timeout) {
        $self->_wait_for(sub { $self->is_started })
            or throw App::CamelPKI::Error::OtherProcess("Cannot start Apache");
    } else {
        while(! $self->is_started) { sleep(1); }
    };
    return;
}

=head2 stop()

Stops the daemon synchronously, meaning that I<stop> will only return
control to its caller after ensuring that the Apache process whose PID
is in the PID file is terminated, and the TCP port is closed.  Like
L</start>, this method is idempotent and returns immediately if the
server was already down.

An exception of class L<App::CamelPKI::Error/App::CamelPKI::Error::OtherProcess>
will be thrown if the server still hasn't stopped after
L</async_timeout> seconds.

Note that the "started" or "stopped" state is persisted to the
filesystem using the usual UNIX PID file mechanism; therefore it is
not necessary to use the same Perl object (or even the same process)
to L</start> and I<stop()> a given server.

=cut

sub stop {
    my ($self) = @_;
    throw App::CamelPKI::Error::OtherProcess("Apache is wedged")
        if ($self->is_wedged);
    return # Not wedged and not started means stopped
        if ! defined(my $pid = $self->_process_ready);

    kill TERM => $pid;

    $self->_wait_for(sub { $self->is_stopped })
        or throw App::CamelPKI::Error::OtherProcess("Cannot stop Apache");
    return;
}

=head2 is_started()

Returns true iff the PID file currently contains the PID of a live
Apache process, B<and> one can connect to the TCP port.

=cut

sub is_started {
    my ($self) = @_;
    $self->_process_ready && $self->_port_ready;
}

=head2 is_stopped()

Returns true iff the PID file (if it exists at all) contains something
that is not the PID of a live Apache process, B<and> the TCP port is
closed.

=cut

sub is_stopped {
    my ($self) = @_;
    (! $self->_process_ready) && (! $self->_port_ready);
}

=head2 is_wedged()

Returns true iff neither L</is_stopped>, nor L</is_started> are true
(e.g. if the TCP port is taken, but not by us).  One cannot call
L</start> or L</stop> against an instance of I<App::CamelPKI::SysV::Apache>



( run in 1.926 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )