aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2019-08-21 16:16:42 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2019-08-23 17:14:09 +0300
commit99d1298ce565acb55514deb472cd5d534d9e8e9b (patch)
tree60f5c8c619563065c3c6dee8dffc7f898b213c79 /lib
parent3610ab59b2085c5eda3933690a973bad1760d3d4 (diff)
downloadacmeman-99d1298ce565acb55514deb472cd5d534d9e8e9b.tar.gz
acmeman-99d1298ce565acb55514deb472cd5d534d9e8e9b.tar.bz2
Move main functionality to a module
* acmeman: Use App::Acmeman. * lib/App/Acmeman.pm: New module. * lib/App/Acmeman/Config.pm (mangle): Reset debug_level if necessary. Use the BOOL data type. * lib/App/Acmeman/Log.pm: New module. * lib/App/Acmeman/Source.pm: Use functions from App::Acmeman::Log. (add): New method. (define_alias): Use add. * lib/App/Acmeman/Source/Apache.pm: Use functions from App::Acmeman::Log. * lib/App/Acmeman/Source/File.pm: Likewise.
Diffstat (limited to 'lib')
-rw-r--r--lib/App/Acmeman.pm541
-rw-r--r--lib/App/Acmeman/Config.pm35
-rw-r--r--lib/App/Acmeman/Log.pm68
-rw-r--r--lib/App/Acmeman/Source.pm25
-rw-r--r--lib/App/Acmeman/Source/Apache.pm46
-rw-r--r--lib/App/Acmeman/Source/File.pm7
6 files changed, 652 insertions, 70 deletions
diff --git a/lib/App/Acmeman.pm b/lib/App/Acmeman.pm
new file mode 100644
index 0000000..c867c1b
--- /dev/null
+++ b/lib/App/Acmeman.pm
@@ -0,0 +1,541 @@
+package App::Acmeman;
+use strict;
+use warnings;
+use Protocol::ACME;
+use Protocol::ACME::Challenge::LocalFile;
+use Crypt::Format;
+use Crypt::OpenSSL::PKCS10 qw(:const);
+use Crypt::OpenSSL::RSA;
+use Crypt::OpenSSL::X509;
+use File::Basename;
+use File::Path qw(make_path);
+use DateTime::Format::Strptime;
+use LWP::UserAgent;
+use LWP::Protocol::https;
+use Socket qw(inet_ntoa);
+use Sys::Hostname;
+use Pod::Usage;
+use Pod::Man;
+use Getopt::Long qw(:config gnu_getopt no_ignore_case auto_version);
+use POSIX qw(strftime time floor);
+use App::Acmeman::Config;
+use App::Acmeman::Domain qw(:files);
+use Data::Dumper;
+use Text::ParseWords;
+use App::Acmeman::Log qw(:all :sysexits);
+use feature 'state';
+
+our $VERSION = '1.11';
+
+my $progdescr = "manages ACME certificates";
+
+my %acme_endpoint = (prod => 'acme-v01.api.letsencrypt.org',
+ staging => 'acme-staging.api.letsencrypt.org');
+my $letsencrypt_root_cert_url =
+ 'https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem';
+
+sub new {
+ my $class = shift;
+ my $self = bless {
+ _progname => basename($0),
+ _acme_host => 'prod',
+ _command => 'renew',
+ _option => {
+ config_file => '/etc/acmeman.conf'
+ },
+ _domains => []
+ }, $class;
+ GetOptions(
+ "h" => sub {
+ pod2usage(-message => "$self->{_progname}: $progdescr",
+ -exitstatus => EX_OK);
+ },
+ "help" => sub {
+ pod2usage(-exitstatus => EX_OK, -verbose => 2);
+ },
+ "usage" => sub {
+ pod2usage(-exitstatus => EX_OK, -verbose => 0);
+ },
+ "debug|d+" => \$self->{_option}{debug},
+ "dry-run|n" => \$self->{_option}{dry_run},
+ "stage|s" => sub { $self->{_acme_host} = 'staging' },
+ "force|F" => \$self->{_option}{force},
+ "time-delta|D=n" => \$self->{_option}{time_delta},
+ "setup|S" => sub { $self->{_command} = 'setup' },
+ "alt-names|a" => \$self->{_option}{check_alt_names},
+ "config-file|f=s" => \$self->{_option}{config_file},
+ ) or exit(EX_USAGE);
+ ++$self->{_option}{debug} if $self->dry_run_option;
+ debug_level($self->{_option}{debug});
+
+ $self->add_selected_domains(@ARGV);
+
+ $self->{_cf} = new App::Acmeman::Config($self->option('config_file'));
+ my $v;
+ if ($v = $self->option('time_delta')) {
+ $self->cf->set(qw(core time-delta), $v);
+ }
+ if ($v = $self->option('check_alt_names')) {
+ $self->cf->set(qw(core check-alt-names), $v);
+ }
+ if ($v = $self->option('debug')) {
+ $self->cf->set(qw(core verbose), $v);
+ } else {
+ $self->option('debug', $self->cf->get(qw(core verbose)));
+ }
+
+ debug_level($self->cf->core->verbose);
+
+ return $self;
+}
+
+sub run {
+ my $self = shift;
+ $self->${ \$self->{_command} }();
+}
+
+sub cf { shift->{_cf} }
+sub progname { shift->{_progname} }
+sub acme_host { $acme_endpoint{shift->{_acme_host}} }
+
+sub option {
+ my ($self,$opt) = @_;
+ return $self->{_option}{$opt};
+}
+
+sub force_option { shift->option('force') }
+sub dry_run_option { shift->option('dry_run') }
+
+sub resolve {
+ my ($self, $host) = @_;
+ if (my @addrs = gethostbyname($host)) {
+ return map { inet_ntoa($_) } @addrs[4 .. $#addrs];
+ } else {
+ error("$host doesn't resolve");
+ }
+ return ();
+}
+
+sub myip {
+ my ($self, $host) = @_;
+ state $ips;
+ unless ($ips) {
+ $ips = {};
+ my $addhost;
+
+ if ($self->cf->is_set(qw(core my-ip))) {
+ $addhost = 0;
+ foreach my $ip ($self->cf->get(qw(core my-ip))) {
+ if ($ip eq '$hostip') {
+ $addhost = 1;
+ } else {
+ $ips->{$ip} = 1;
+ }
+ }
+ } else {
+ $addhost = 1;
+ }
+
+ if ($addhost) {
+ foreach my $ip ($self->resolve(hostname())) {
+ $ips->{$ip} = 1;
+ }
+ }
+ }
+ return $ips->{$host};
+}
+
+sub host_ns_ok {
+ my ($self, $host) = @_;
+ foreach my $ip ($self->resolve($host)) {
+ return 1 if $self->myip($ip);
+ }
+ return 0
+}
+
+sub prep_dir {
+ my ($self, $name) = @_;
+ my $dir = dirname($name);
+ if (! -d $dir) {
+ debug(3, "creating directory $dir");
+ my @created = make_path("$dir", { error => \my $err } );
+ if (@$err) {
+ for my $diag (@$err) {
+ my ($file, $message) = %$diag;
+ if ($file eq '') {
+ error($message);
+ } else {
+ error("mkdir $file: $message");
+ }
+ }
+ exit(EX_CANTCREAT);
+ }
+ }
+}
+
+sub get_root_cert {
+ my $self = shift;
+ my $name = shift;
+
+ $self->prep_dir($name) unless $self->dry_run_option;
+
+ debug(1, "downloading $letsencrypt_root_cert_url to \"$name\"");
+ my $ua = LWP::UserAgent->new;
+ my $response = $ua->get($letsencrypt_root_cert_url);
+ if ($response->is_success) {
+ unless ($self->dry_run_option) {
+ open(my $fd, '>', $name)
+ or abend(EX_CANTCREAT,
+ "can't open \"$name\" for writing: $!");
+ print $fd $response->decoded_content;
+ close $fd;
+ }
+ } else {
+ error("error downloading certificate from $letsencrypt_root_cert_url");
+ abend(EX_NOINPUT, $response->status_line);
+ }
+}
+
+sub setup {
+ my $self = shift;
+
+ $self->prep_dir($self->cf->get(qw(core rootdir)).'/file');
+
+ $self->get_root_cert('/etc/ssl/acme/lets-encrypt-x3-cross-signed.pem');
+
+ foreach my $src ($self->cf->get(qw(core source))) {
+ unless ($src->setup(dry_run => $self->dry_run_option,
+ force => $self->force_option)) {
+ exit(1);
+ }
+ }
+
+ exit(EX_OK);
+}
+
+sub collect {
+ my $self = shift;
+ my $err;
+ my $node = $self->cf->getnode('domain') or return;
+ my $subs = $node->as_hash;
+ while (my ($k, $v) = each %$subs) {
+ my $dom;
+ my $ft;
+
+ my $alt = [grep { !$self->cf->get(qw(core check-dns))
+ || $self->host_ns_ok($_) }
+ ($k, ($v->{alt} ? @{$v->{alt}} : ()))];
+ if (@$alt) {
+ $k = shift @$alt;
+ $alt = undef unless @$alt;
+ } else {
+ error("ignoring $k: none of its names resolves to our IP");
+ next;
+ }
+
+ if (exists($v->{files})) {
+ if (my $fref = $self->cf->getnode('files', $v->{files})) {
+ $dom = new App::Acmeman::Domain(
+ cn => $k,
+ alt => $alt,
+ postrenew => $v->{postrenew},
+ %{$fref->as_hash});
+ } else {
+ error("files.$v->{files} is referenced from [domain $k], but never declared");
+ ++$err;
+ next;
+ }
+ } else {
+ $dom = new App::Acmeman::Domain(
+ cn => $k,
+ alt => $alt,
+ postrenew => $v->{postrenew},
+ %{$self->cf->getnode('files', $self->cf->get(qw(core files)))->as_hash});
+ }
+ $self->domains($dom);
+ }
+ exit(1) if $err;
+}
+
+sub domains {
+ my $self = shift;
+ if (@_) {
+ push @{$self->{_domains}}, @_;
+ }
+ return @{$self->{_domains}};
+}
+
+sub coalesce {
+ my $self = shift;
+ debug(2, "coalescing virtual hosts");
+ my $i = 0;
+ my @domlist;
+ foreach my $ent (sort { $a->{domain} cmp $b->{domain} }
+ map { { ord => $i++, domain => $_ } } $self->domains) {
+ if (@domlist && $domlist[-1]->{domain}->cn eq $ent->{domain}->cn) {
+ $domlist[-1]->{domain} += $ent->{domain};
+ } else {
+ push @domlist, $ent;
+ }
+ }
+ @{$self->{_domains}} =
+ map { $_->{domain} } sort { $a->{ord} <=> $b->{ord} } @domlist;
+}
+
+sub add_selected_domains {
+ my $self = shift;
+ if (@_) {
+ @{$self->{_selection}}{map { lc } @_} = (1) x @_;
+ }
+}
+
+sub selected_domains {
+ my $self = shift;
+ return $self->domains unless $self->{_selection};
+ return grep { $self->{_selection}{$_} } $self->domains;
+}
+
+sub challenge { shift->{_challenge} }
+
+sub renew {
+ my $self = shift;
+
+ $self->collect;
+ unless ($self->selected_domains) {
+ debug(1, "nothing to do");
+ exit(0);
+ }
+ $self->coalesce;
+
+ $self->{_challenge} = Protocol::ACME::Challenge::LocalFile->new({
+ www_root => $self->cf->get(qw(core rootdir))
+ });
+
+ my $renewed = 0;
+ foreach my $vhost ($self->selected_domains) {
+ if ($self->force_option || $self->domain_cert_expires($vhost)) {
+ if ($self->register_domain_certificate($vhost)) {
+ if (my $cmd = $vhost->postrenew) {
+ $self->runcmd($cmd);
+ } else {
+ $renewed++;
+ }
+ }
+ }
+ }
+
+ if ($renewed) {
+ if ($self->cf->is_set(qw(core postrenew))) {
+ foreach my $cmd ($self->cf->get(qw(core postrenew))) {
+ $self->runcmd($cmd);
+ }
+ } else {
+ error("certificates changed, but no postrenew command is defined (core.postrenew)");
+ }
+ }
+}
+
+sub domain_cert_expires {
+ my ($self, $domain) = @_;
+ my $crt = $domain->certificate_file;
+ if (-f $crt) {
+ my $x509 = Crypt::OpenSSL::X509->new_from_file($crt);
+
+ my $exts = $x509->extensions_by_name();
+ if (exists($exts->{subjectAltName})) {
+ my $msg = $self->cf->get(qw(core check-alt-names))
+ ? 'will renew' : 'use -a to trigger renewal';
+ my @names = map { s/^DNS://; $_ }
+ split /,\s*/, $exts->{subjectAltName}->to_string();
+ my @missing;
+ foreach my $vh (sort { length($b) <=> length($a) } $domain->names) {
+ unless (grep { $_ eq $vh } @names) {
+ push @missing, $vh;
+ }
+ }
+ if (@missing) {
+ debug(1, "$crt: the following SANs are missing: "
+ . join(', ', @missing)
+ . "; $msg");
+ return 1 if $self->cf->get(qw(core check-alt-names));
+ }
+ }
+
+ my $expiry = $x509->notAfter();
+
+ my $strp = DateTime::Format::Strptime->new(
+ pattern => '%b %d %H:%M:%S %Y %Z',
+ time_zone => 'GMT'
+ );
+ my $ts = $strp->parse_datetime($expiry)->epoch;
+ my $now = time();
+ if ($now < $ts) {
+ my $hours = floor(($ts - $now) / 3600);
+ my $in;
+ if ($hours > 24) {
+ my $days = floor($hours / 24);
+ $in = "in $days days";
+ } elsif ($hours == 24) {
+ $in = "in one day";
+ } else {
+ $in = "today";
+ }
+ debug(2, "$crt expires on $expiry, $in");
+ if ($now + $self->cf->get(qw(core time-delta)) < $ts) {
+ return 0;
+ } else {
+ debug(2, "will renew $crt (expires on $expiry, $in)");
+ }
+ } else {
+ debug(2, "will renew $crt");
+ }
+ }
+ return 1;
+}
+
+sub debug_to_loglevel {
+ my $self = shift;
+ my @lev = ('err', 'info', 'debug');
+ my $v = $self->cf->core->verbose;
+ return $lev[$v > $#lev ? $#lev : $v];
+}
+
+sub register_domain_certificate {
+ my ($self,$domain) = @_;
+
+ my $key_size = $self->cf->get('domain', $domain, 'key-size')
+ || $self->cf->get('core', 'key-size');
+
+ if ($self->cf->core->verbose > 0) {
+ my $crt = $domain->certificate_file;
+ my $alt = join(',', $domain->alt);
+ if (-f $crt) {
+ debug(1, "renewing $crt: CN=$domain, alternatives=$alt, key_size=$key_size");
+ } else {
+ debug(1, "issuing $crt: CN=$domain, alternatives=$alt, key_size=$key_size");
+ }
+ }
+
+ return 1 if $self->dry_run_option;
+ my $account_key = Crypt::OpenSSL::RSA->generate_key($key_size);
+
+ my $acme = Protocol::ACME->new(
+ host => $self->acme_host,
+ account_key => { buffer => $account_key->get_private_key_string(),
+ format => 'PEM' },
+ loglevel => $self->debug_to_loglevel()
+ );
+
+ eval {
+ $acme->directory();
+ $acme->register();
+ $acme->accept_tos();
+
+ foreach my $name ($domain->names) {
+ $acme->authz($name);
+ $acme->handle_challenge($self->challenge);
+ $acme->check_challenge();
+ $acme->cleanup_challenge($self->challenge);
+ }
+
+ my $csr = $self->make_csr($domain, $key_size);
+ my $cert = $acme->sign({ format => 'PEM',
+ buffer => $csr->get_pem_req() });
+ my $chain = $acme->chain();
+
+ if (my $filename = $domain->file(KEY_FILE)) {
+ debug(3, "writing $filename");
+ $self->prep_dir($filename);
+ my $u = umask(077);
+ $csr->write_pem_pk($filename);
+ umask($u);
+
+ if ($filename = $domain->file(CA_FILE)) {
+ $self->save_crt($domain, CA_FILE, $chain);
+ }
+ $self->save_crt($domain, CERT_FILE, $cert);
+ } else {
+ $filename = $domain->certificate_file;
+ debug(3, "writing $filename");
+ $self->prep_dir($filename);
+ my $u = umask(077);
+ open(my $fd, '>', $filename)
+ or abend(EX_CANTCREAT,
+ "can't open $filename for writing: $!");
+ print $fd Crypt::Format::der2pem($cert, 'CERTIFICATE');
+ print $fd "\n";
+ print $fd Crypt::Format::der2pem($chain, 'CERTIFICATE');
+ print $fd "\n";
+ print $fd $csr->get_pem_pk();
+ print $fd "\n";
+ umask($u);
+ }
+ };
+ if ($@) {
+ if (UNIVERSAL::isa($@, 'Protocol::ACME::Exception')) {
+ error("$domain: can't renew certificate: $@->{status}");
+ if (exists($@->{error})) {
+ error("$domain: $@->{error}{status} $@->{error}{detail}");
+ } else {
+ error("$domain: $@->{detail} $@->{type}");
+ }
+ } elsif (ref($@) eq '') {
+ chomp $@;
+ error("$domain: failed to renew certificate: $@");
+ } else {
+ error("$domain: failed to renew certificate");
+ print STDERR Dumper([$@]);
+ }
+ return 0;
+ }
+ return 1;
+}
+
+sub make_csr {
+ my ($self, $dom, $keysize) = @_;
+ my $req = Crypt::OpenSSL::PKCS10->new($keysize);
+ $req->set_subject("/CN=".$dom->cn);
+ $req->add_ext(Crypt::OpenSSL::PKCS10::NID_subject_alt_name,
+ join(',', map { "DNS:$_" } $dom->alt))
+ if $dom->alt > 0;
+ $req->add_ext_final();
+ $req->sign();
+ return $req;
+}
+
+sub save_crt {
+ my $self = shift;
+ my $domain = shift;
+ my $type = shift;
+
+ if (my $filename = $domain->file($type)) {
+ debug(3, "writing $filename");
+ $self->prep_dir($filename);
+ open(my $fd, '>', $filename);
+
+ foreach my $der (@_) {
+ my $pem = Crypt::Format::der2pem($der, 'CERTIFICATE');
+ print $fd $pem;
+ print $fd "\n";
+ }
+ close $fd;
+ return $filename;
+ }
+}
+
+sub runcmd {
+ my ($self,$cmd) = @_;
+ debug(3, "running $cmd");
+ unless ($self->dry_run_option) {
+ system($cmd);
+ if ($? == -1) {
+ error("$cmd: failed to execute: $!");
+ } elsif ($? & 127) {
+ error("$cmd: died on signal ".($? & 127));
+ } elsif (my $code = ($? >> 8)) {
+ error("$cmd: exited with code $code");
+ }
+ }
+}
+
+1;
diff --git a/lib/App/Acmeman/Config.pm b/lib/App/Acmeman/Config.pm
index 6860428..25c940f 100644
--- a/lib/App/Acmeman/Config.pm
+++ b/lib/App/Acmeman/Config.pm
@@ -5,30 +5,7 @@ use warnings;
use Carp;
use parent 'Config::Parser::Ini';
use Text::ParseWords;
-
-use constant EX_CONFIG => 78;
-
-sub check_bool {
- my ($self, $vref, $prev_value, $locus) = @_;
- my %bt = (
- 0 => 0,
- off => 0,
- false => 0,
- no => 0,
- 1 => 1,
- on => 1,
- true => 1,
- yes => 1
- );
- my $res = $bt{lc($$vref)};
- unless (defined($res)) {
- $self->error("not a boolean: $$vref");
- return 0;
- }
-
- $$vref = $res;
- return 1;
-}
+use App::Acmeman::Log qw(debug_level :sysexits);
sub new {
my $class = shift;
@@ -48,6 +25,9 @@ sub mangle {
my $self = shift;
my $err;
+ if (debug_level() == 0 && $self->core->verbose) {
+ debug_level($self->core->verbose);
+ }
$self->set(qw(core files default))
unless $self->is_set(qw(core files));
@@ -123,8 +103,6 @@ sub mangle {
exit(EX_CONFIG) if $err;
}
-
-
1;
__DATA__
[core]
@@ -133,10 +111,11 @@ __DATA__
files = STRING
time-delta = NUMBER :default=86400
source = STRING :default=apache :array
- check-alt-names = STRING :default=0 :check=check_bool
- check-dns = STRING :default=1 :check=check_bool
+ check-alt-names = BOOL :default=0
+ check-dns = BOOL :default=1
my-ip = STRING :array
key-size = NUMBER :default=4096
+ verbose = NUMBER
[files ANY]
type = STRING :re="^(single|split)$"
certificate-file = STRING
diff --git a/lib/App/Acmeman/Log.pm b/lib/App/Acmeman/Log.pm
new file mode 100644
index 0000000..72242c0
--- /dev/null
+++ b/lib/App/Acmeman/Log.pm
@@ -0,0 +1,68 @@
+package App::Acmeman::Log;
+use File::Basename;
+use parent 'Exporter';
+
+my @exv = qw(
+ EX_OK
+ EX_USAGE
+ EX_DATAERR
+ EX_NOINPUT
+ EX_SOFTWARE
+ EX_OSFILE
+ EX_CANTCREAT
+ EX_NOPERM
+ EX_CONFIG
+);
+
+my @fnv = qw(error debug abend debug_level);
+
+our @EXPORT_OK = (@fnv, @exv);
+
+our %EXPORT_TAGS = (
+ 'all' => [@fnv],
+ 'sysexits' => [@exv]);
+
+our $progname = basename($0);
+our $debug_level = 0;
+
+use constant {
+ EX_OK => 0,
+ EX_USAGE => 64,
+ EX_DATAERR => 65,
+ EX_NOINPUT => 66,
+ EX_SOFTWARE => 70,
+ EX_OSFILE => 72,
+ EX_CANTCREAT => 73,
+ EX_NOPERM => 77,
+ EX_CONFIG => 78
+};
+
+sub debug_level {
+ my $lev = shift;
+ if ($lev) {
+ $debug_level = $lev;
+ }
+ $debug_level;
+}
+
+sub error {
+ my $msg = shift;
+ local %_ = @_;
+ print STDERR "$progname: ";
+ print STDERR "$_{prefix}: " if defined($_{prefix});
+ print STDERR "$msg\n"
+}
+
+sub debug {
+ my $l = shift;
+ error(join(' ',@_), prefix => 'DEBUG') if $debug_level >= $l;
+}
+
+sub abend {
+ my $code = shift;
+ error(@_);
+ exit $code;
+}
+
+1;
+
diff --git a/lib/App/Acmeman/Source.pm b/lib/App/Acmeman/Source.pm
index 391daca..de1a666 100644
--- a/lib/App/Acmeman/Source.pm
+++ b/lib/App/Acmeman/Source.pm
@@ -4,22 +4,6 @@ use strict;
use warnings;
use Carp;
-sub debug {
- my $self = shift;
- if (defined(&::debug)) {
- ::debug(@_);
- }
-}
-
-sub error {
- my $self = shift;
- if (exists($self->{_cfg})) {
- $self->{_cfg}->error(@_);
- } else {
- carp @_;
- }
-}
-
sub set {
my $self = shift;
croak "improper use of the set method"
@@ -27,6 +11,13 @@ sub set {
return $self->{_cfg}->set(@_);
}
+sub add {
+ my $self = shift;
+ croak "improper use of the add method"
+ unless exists $self->{_cfg};
+ return $self->{_cfg}->add_value(@_);
+}
+
sub get {
my $self = shift;
croak "improper use of the get method"
@@ -44,7 +35,7 @@ sub define_alias {
my $self = shift;
my $cn = shift || croak "domain name must be given";
foreach my $alias (@_) {
- $self->set('domain', $cn, 'alt', $alias);
+ $self->add(['domain', $cn, 'alt'], $alias);
}
}
diff --git a/lib/App/Acmeman/Source/Apache.pm b/lib/App/Acmeman/Source/Apache.pm
index b429f89..f35c267 100644
--- a/lib/App/Acmeman/Source/Apache.pm
+++ b/lib/App/Acmeman/Source/Apache.pm
@@ -8,6 +8,7 @@ use File::Path qw(make_path);
use File::Spec;
use IPC::Open3;
use App::Acmeman::Apache::Layout;
+use App::Acmeman::Log qw(:all);
use parent 'App::Acmeman::Source';
use Getopt::Long qw(GetOptionsFromArray :config gnu_getopt no_ignore_case);
@@ -25,7 +26,7 @@ sub layout { shift->{_layout} }
sub scan {
my ($self) = @_;
- $self->debug(2, 'assuming Apache layout "'.$self->layout->name.'"');
+ debug(2, 'assuming Apache layout "'.$self->layout->name.'"');
$self->set(qw(core postrenew), $self->layout->restart_command);
return $self->examine_http_config($self->layout->config_file);
}
@@ -50,7 +51,7 @@ sub examine_http_config {
state $state = STATE_INITIAL;
- $self->debug(3, "reading apache configuration file \"$file\"");
+ debug(3, "reading apache configuration file \"$file\"");
if (open(my $fd, '<', $file)) {
my $server_name;
my @server_aliases;
@@ -63,7 +64,7 @@ sub examine_http_config {
s/^\s+//;
next if /^(#.*)?$/;
if (/^include(optional)?\s+(.+?)\s*$/i) {
- #$self->debug(3, "$file:$line: state $state: Include".($1||'')." $2");
+ #debug(3, "$file:$line: state $state: Include".($1||'')." $2");
$self->http_include($self->dequote($2), defined($1));
next;
}
@@ -93,7 +94,7 @@ sub examine_http_config {
my $cn = shift @server_aliases;
$self->define_domain($cn);
$self->define_alias($cn, @server_aliases);
- $self->debug(3, "$file:$line: will handle ".
+ debug(3, "$file:$line: will handle ".
join(',', $cn, @server_aliases));
} elsif ($state == STATE_USE_REFERENCE) {
$self->set('domain', $reference,
@@ -106,18 +107,18 @@ sub examine_http_config {
if ($state == STATE_VIRTUAL_HOST) {
$state = STATE_USE_CHALLENGE;
} elsif ($state == STATE_USE_CHALLENGE) {
- $self->error("$file:$line: duplicate use of LetsEncryptChallenge");
+ error("$file:$line: duplicate use of LetsEncryptChallenge");
} else {
- $self->error("$file:$line: LetsEncryptChallenge and LetsEncryptReference can't be used together");
+ error("$file:$line: LetsEncryptChallenge and LetsEncryptReference can't be used together");
}
} elsif (/^(?:(?i)Use)\s+LetsEncryptReference\s+(.+?)\s*$/) {
if ($state == STATE_VIRTUAL_HOST) {
$state = STATE_USE_REFERENCE;
$reference = $1;
} elsif ($state == STATE_USE_REFERENCE) {
- $self->error("$file:$line: duplicate use of LetsEncryptReference");
+ error("$file:$line: duplicate use of LetsEncryptReference");
} else {
- $self->error("$file:$line: LetsEncryptChallenge and LetsEncryptReference can't be used together");
+ error("$file:$line: LetsEncryptChallenge and LetsEncryptReference can't be used together");
}
} elsif (/^(?:(?i)ServerName)\s+(\S+)/) {
$server_name = $self->dequote($1);
@@ -133,7 +134,7 @@ sub examine_http_config {
my $dir = $self->dequote($1);
$dir =~ s{/.well-known/acme-challenge$}{};
$self->set(qw(core rootdir), $dir);
- $self->debug(3, "ACME challenge root dir: $dir");
+ debug(3, "ACME challenge root dir: $dir");
}
} elsif ($state == STATE_MACRO_SSL) {
if (m{^</macro}i) {
@@ -150,7 +151,7 @@ sub examine_http_config {
}
close $fd;
} else {
- $self->error("can't open file \"$file\": $!");
+ error("can't open file \"$file\": $!");
return 0;
}
return 1;
@@ -177,7 +178,7 @@ sub http_include {
$pattern = File::Spec->catfile($pattern, '*') if -d $pattern;
foreach my $file (glob $pattern) {
if ($optional && ! -e $file) {
- $self->debug(1, "optional include file \"$file\" doesn't exist");
+ debug(1, "optional include file \"$file\" doesn't exist");
next;
}
$self->examine_http_config($file);
@@ -191,9 +192,9 @@ sub mkpath {
for my $diag (@$err) {
my ($file, $message) = %$diag;
if ($file eq '') {
- $self->error($message);
+ error($message);
} else {
- $self->error("mkdir $file: $message");
+ error("mkdir $file: $message");
}
}
return 0;
@@ -206,16 +207,16 @@ sub setup {
my $filename = $self->layout->incdir() . "/httpd-letsencrypt.conf";
if (-e $filename) {
if ($args{force}) {
- ::error("the file \"$filename\" already exists",
- prefix => 'warning');
+ error("the file \"$filename\" already exists",
+ prefix => 'warning');
} else {
- ::error("the file \"$filename\" already exists");
- ::error("use --force to continue");
+ error("the file \"$filename\" already exists");
+ error("use --force to continue");
return 0;
}
}
my $www_root = $self->get(qw(core rootdir));
- $self->debug(2, "writing $filename");
+ debug(2, "writing $filename");
unless ($args{dry_run}) {
unless ($self->mkpath($self->layout->incdir())) {
return 0;
@@ -264,8 +265,9 @@ EOT
}
}
- ::error("created file \"$filename\"", prefix => 'note');
- ::error("please, enable mod_macro and make sure your Apache configuration includes this file", prefix => 'note');
+ error("created file \"$filename\"", prefix => 'note');
+ error("please, enable mod_macro and make sure your Apache configuration includes this file",
+ prefix => 'note');
return 1;
}
@@ -296,8 +298,8 @@ sub probe {
close $nullin;
close $nullout;
unless ($self->server_root) {
- ::error("can't deduce server root directory");
- ::error("use `source = apache --server-root=DIR' in [core] section of /etc/acmeman.conf to declare it");
+ error("can't deduce server root directory");
+ error("use `source = apache --server-root=DIR' in [core] section of /etc/acmeman.conf to declare it");
exit(1);
}
}
diff --git a/lib/App/Acmeman/Source/File.pm b/lib/App/Acmeman/Source/File.pm
index 9414e86..c622d51 100644
--- a/lib/App/Acmeman/Source/File.pm
+++ b/lib/App/Acmeman/Source/File.pm
@@ -6,6 +6,7 @@ use Carp;
use File::Spec;
use parent 'App::Acmeman::Source';
use Getopt::Long qw(GetOptionsFromArray :config gnu_getopt no_ignore_case);
+use App::Acmeman::Log qw(:all);
sub new {
my $class = shift;
@@ -28,7 +29,7 @@ sub new {
sub scan {
my ($self) = @_;
- $self->debug(1, "initializing file list from $self->{pattern}");
+ debug(1, "initializing file list from $self->{pattern}");
my $err = 0;
if ($self->{host}) {
$self->define_domain($self->{host});
@@ -42,10 +43,10 @@ sub scan {
sub load {
my ($self, $file) = @_;
- $self->debug(1, "reading $file");
+ debug(1, "reading $file");
open(my $fh, '<', $file)
or do {
- $self->error("can't open $file: $!");
+ error("can't open $file: $!");
return 0;
};
chomp(my @lines = <$fh>);

Return to:

Send suggestions and report system problems to the System administrator.