#!/usr/bin/perl

use strict;
use warnings;

use IO::File;
use File::Find;
use File::stat;

use PVE::APLInfo;
use PVE::CertHelpers;
use PVE::Certificate;
use PVE::Cluster::Setup;
use PVE::Cluster;
use PVE::DataCenterConfig;
use PVE::INotify;
use PVE::NodeConfig;
use PVE::RPCEnvironment;
use PVE::SafeSyslog;
use PVE::Tools;

use PVE::API2::ACME;
use PVE::API2::APT;
use PVE::API2::Subscription;

initlog('pveupdate', 'daemon');

die "please run as root\n" if $> != 0;

$ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';

PVE::INotify::inotify_init();

my $rpcenv = PVE::RPCEnvironment->init('cli');

$rpcenv->init_request();
$rpcenv->set_language($ENV{LANG});
$rpcenv->set_user('root@pam');

my $nodename = PVE::INotify::nodename();

eval { PVE::API2::Subscription->update({ node => $nodename }); };
if (my $err = $@) {
    syslog('err', "update subscription info failed: $err");
}

my $dccfg = PVE::Cluster::cfs_read_file('datacenter.cfg');
eval { PVE::APLInfo::update($dccfg->{http_proxy}); };
if (my $err = $@) {
    syslog('err', "update appliance info failed - see /var/log/pveam.log for details");
}

my $info = eval { PVE::API2::Subscription::read_etc_subscription() };

my $notify_on = $dccfg->{notify}->{'package-updates'} // 'auto';
my $notify = 0;
if ($notify_on eq 'auto') {
    # hosts with subscriptions are likely production system and thus want infos about new packages
    $notify = ($info && $info->{status} eq 'active') ? 1 : 0;
} elsif ($notify_on eq 'always') {
    $notify = 1;
} elsif ($notify_on eq 'never') {
    $notify = 0;
} else {
    warn "unexpected package-updates  notify configuration value '$notify_on'\n";
}
eval { PVE::API2::APT->update_database({ node => $nodename, notify => $notify, quiet => 1 }); };
if (my $err = $@) {
    syslog('err', "update apt database failed: $err");
}

eval {
    my $node_config = PVE::NodeConfig::load_config($nodename);
    my $acme_node_config = PVE::NodeConfig::get_acme_conf($node_config);
    if ($acme_node_config && $acme_node_config->{domains}) {
        my $cert = PVE::CertHelpers::cert_path_prefix($nodename) . ".pem";
        if (-e $cert) {
            if (PVE::Certificate::check_expiry($cert, time() + 30 * 24 * 60 * 60)) {
                PVE::API2::ACME->renew_certificate({ node => $nodename });
            } else {
                syslog(
                    'info',
                    'Custom certificate does not expire soon, skipping ACME renewal.',
                );
            }
        } else {
            syslog(
                'info',
                'ACME config found for node, but no custom certificate exists. Skipping ACME renewal until initial certificate has been deployed.',
            );
        }
    }
};
syslog('err', "Renewing ACME certificate failed: $@") if $@;

eval {
    my $certpath = PVE::CertHelpers::default_cert_path_prefix($nodename) . ".pem";
    my $capath = "/etc/pve/pve-root-ca.pem";

    my $renew = sub {
        my ($msg) = @_;

        # get CA info
        my $cainfo = PVE::Certificate::get_certificate_info($capath);

        # get cert and check issuer and chain metadata
        my $certinfo = PVE::Certificate::get_certificate_info($certpath);
        if ($certinfo->{issuer} ne $cainfo->{subject}) {
            die "SSL certificate ($certpath) is not issued by root CA ($capath)!\n";
        }

        # check if cert is really signed by the ca
        # TODO: replace by low level ssleay interface if version 1.86 is available
        PVE::Tools::run_command(['/usr/bin/openssl', 'verify', '-CAfile', $capath, $certpath]);

        print "PVE certificate $msg\n";
        # create new certificate
        my $ip = PVE::Cluster::remote_node_ip($nodename);
        PVE::Cluster::Setup::gen_pve_ssl_cert(1, $nodename, $ip);

        print "Restarting pveproxy after renewing certificate\n";
        PVE::Tools::run_command(['systemctl', 'reload-or-restart', 'pveproxy']);
    };

    if (PVE::Certificate::check_expiry($certpath, time() + 14 * 24 * 60 * 60)) {
        # expires in next 2 weeks
        $renew->("expires soon, renewing...");
    } elsif (!PVE::Certificate::check_expiry($certpath, time() + 2 * 365 * 24 * 60 * 60)) {
        # expires in more than 2 years
        $renew->(
            "expires in more than 2 years, renewing to reduce certificate life-span for client compatibility..."
        );
    }
};
syslog('err', "Checking/Renewing SSL certificate failed: $@") if $@;

sub cleanup_tasks {

    my $taskdir = "/var/log/pve/tasks";
    my $filename = "$taskdir/index.1";

    my $fh = IO::File->new($filename, O_RDONLY);
    return if !$fh;

    my $endtime = 0;
    while (defined(my $line = <$fh>)) {
        if ($line =~ m/^(\S+)(\s([0-9A-Za-z]{8})(\s(\S.*))?)?$/) {
            $endtime = hex($3);
            last;
        }
    }
    close($fh);

    return if !$endtime;

    # print "delete task older that $endtime\n" . localtime($endtime) . "\n";

    my $count = 0;

    my $wanted = sub {
        my $filename = $_;

        return if $filename !~ m/^UPID:/;

        my $st;
        if (($st = stat($filename)) && ($st->mtime < $endtime)) {
            unlink($filename);
            $count++;
        }
    };

    foreach my $subdir (qw(0 1 2 3 4 5 6 7 8 9 A B C D E F)) {
        my $path = "$taskdir/$subdir";
        find($wanted, $path);
    }

    if ($count) {
        syslog('info', "cleanup removed $count task logs");
    }
}

cleanup_tasks();

exit(0);
