ONO::Cron::CronSync

package ONO::Cron::CronSync;
################################################################################
# COPYRIGHT / LICENSE #
################################################################################
#
# This file is part of the ONO Software Project.
#
# Copyright (C) 2000-2025 Jos KIRPS [ www.kirps.com | jos_AT_kirps_DOT_com ]
# and The Joopita Project [ www.joopita.org | contact_AT_joopita_DOT_com ]
#
# This file, as well as other parts of the ONO Software Project or related
# elements, are FREE SOFTWARE available under the ARTISTIC LICENSE 2.0.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# For the full license, see /ono/osr/license/LICENSE.txt, or write to
# jos_AT_kirps_DOT_com or contact_AT_joopita_DOT_com.
#
################################################################################
# END OF COPYRIGHT / LICENSE, HERE COMES THE CODE ... #
################################################################################

use strict;

use ONO::IO;
use ONO::DB;

use ONO::Core;
use ONO::Core::HostOS;
use ONO::Core::HostIO;

use ONO::ToolBox::Docs;
use ONO::ToolBox::LoadBalancer;
use ONO::ToolBox::Logfile;
use ONO::ToolBox::SendMail;
use ONO::ToolBox::System;

use ONO::FW::User::Init;
use ONO::FW::User::Bot;
use ONO::FW::IBS::Network;

use ONO::Lib::DateTime::ToolBox;
use ONO::Lib::Code::RandomID;
use ONO::Lib::Image::Magick;

use ONO::Cron::CronSync::Apache;
use ONO::Cron::CronSync::Backup;
use ONO::Cron::CronSync::Bot;
use ONO::Cron::CronSync::DBMS;
use ONO::Cron::CronSync::FileServer;
use ONO::Cron::CronSync::Monitor;
use ONO::Cron::CronSync::ONO;
use ONO::Cron::CronSync::OS;
use ONO::Cron::CronSync::Users;
use ONO::Cron::CronSync::Web;
use ONO::Cron::CronSync::ToolBox;

###############################################################################
# ONO
###############################################################################

#: CronSync is ONO's configurable cron service, allowing to run a number of
#: important features in the background without having to configure anything
#: manually. All jobs can the set up using the ONO Admin tools.

sub run {

#: The main CronSync module, used to launch all other stuff.

print "\n[RUN] ONO CronSync\n";
print " I'm here: $FindBin::Bin/cronsync.pl\n";

my %vars;
my $vars_ref = \%vars;

my (
$sec,$min,$hour,
$mday,$mon,$year,
$wday,$yday,$timestamp,
) = ONO::Lib::DateTime::ToolBox->get;
my $weekday = ucfirst ONO::Lib::DateTime::ToolBox->day_name($wday);

my $vpath = ONO::IO->path;

print " Document Root: $vpath\n";
print " Current time: $hour:$min:$sec ($mday.$mon.$year)\n";
print " Weekday: $weekday ($wday) / UNIX timestamp: $timestamp secs\n";

my $CRON_DIR = ONO::Cron::CronSync::ToolBox->dir("c","$FindBin::Bin/cronsync.pl");

ONO::IO->mkpath("var/ono/data",777);
ONO::IO->mkdir("$CRON_DIR",777);
ONO::IO->mkdir("$CRON_DIR/lastrun",777);
ONO::IO->store("$CRON_DIR/lastrun/cronsync.txt",$timestamp);

my $switches;
if ($ARGV[0] =~ /^-/) {
$switches = $ARGV[0];
$ARGV[0] = $ARGV[1];
}

if ($switches =~ /v/) {
print " Verbose is ON:\n";
} else {
print " Verbose is OFF (I'll be silent now...)\n[BYE]\n\n";
}

my ($DATA,$MAIL,%TYPE);
foreach my $type (ONO::Cron::CronSync::ToolBox->types("website","users","database","backup","server")) {
$type =~ /^(.*?):(.*)$/;
$TYPE{$1} = $2;
}

my $TIMESTAMP = ONO::Cron::CronSync::ToolBox->timestamp;

$DATA .= ONO::Cron::CronSync::ToolBox->print("ONO CronSync has been launched [$TIMESTAMP]",$switches);
$DATA .= ONO::Cron::CronSync::ToolBox->print("Current SysLoad is ".ONO::IO->sysload()."\%",$switches);

foreach my $job (ONO::IO->dir("$CRON_DIR/jobs")) {
if ($job =~ s/\.txt//) {

$DATA .= ONO::Cron::CronSync::ToolBox->print("Looking for a job, found '$job'...",$switches);

my %data;
foreach my $dat (ONO::IO->list("$CRON_DIR/jobs/$job.txt")) {
if ($dat =~ /^(.*?):(.*)$/) {
$data{$1} = $2;
}
}

if (!$data{'title'}) {
$data{'title'} = $job;
}

$DATA .= ONO::Cron::CronSync::ToolBox->print("Job '$data{'title'}': START [ID:$job]",$switches);

if ($data{'status'} ne "online") {
$DATA .= ONO::Cron::CronSync::ToolBox->print("Job '$data{'title'}': OFFLINE - ABORT",$switches);
} else {
$DATA .= ONO::Cron::CronSync::ToolBox->print("Job '$data{'title'}': ONLINE, checking type and timeframe...",$switches);
if (
$data{'year'} ne "" && $data{'mon'} ne "" && $data{'mday'} ne "" &&
$data{'wday'} ne "" && $data{'hour'} ne "" && $data{'min'} ne "" &&
($data{'year'} eq "*" || $data{'year'} == $year ) &&
($data{'mon'} eq "*" || $data{'mon'} == $mon ) &&
($data{'mday'} eq "*" || $data{'mday'} == $mday) &&
($data{'wday'} eq "*" || $data{'wday'} == $wday) &&
($data{'hour'} eq "*" || $data{'hour'} == $hour) &&
($data{'min'} eq "*" || $data{'min'} == $min) &&
($data{'option'} ne "t" || ONO::IO->exists("$CRON_DIR/trigger/apache-$job.txt"))
) {

$DATA .= ONO::Cron::CronSync::ToolBox->print("Job '$data{'title'}': Type ($data{'type'}) and timeframe looking good",$switches);

my $apache_trigger_mode;

if ($data{'option'} eq "t" && ONO::IO->exists("$CRON_DIR/trigger/apache-$job.txt")) {
$DATA .= ONO::Cron::CronSync::ToolBox->print("Job '$data{'title'}': Removing trigger file - $CRON_DIR/trigger/apache-$job.txt",$switches);
ONO::IO->rm("$CRON_DIR/trigger/apache-$job.txt");
$apache_trigger_mode++;
}

my ($run,$abort) = (1,0);

if ($data{'option'} eq "d") {
$abort = 1;
if ($min == 1 && $hour == 1) {
$abort = 0;
}
}
if ($data{'option'} eq "h" && $min != 0) {
$abort = 1;
}
if ($data{'option'} eq "q" && $min%15 != 0) {
$abort = 1;
}
if ($data{'option'} eq "f" && $min%5 != 0) {
$abort = 1;
}

if ($data{'option'} eq "w") {

$abort = 1;

for (my $w = 0; $w < 16; $w++) {
$data{"monitor_script_$w"} =~ s~[^A-Za-z0-9\:\.\/\_\-]~~g;

if ($data{"monitor_script_$w"} =~ m~^(http|https)://(.*?)\.(.*?)/(.*)$~) {

my $HTML = ONO::IO->exec2(qq~curl -k $data{"monitor_script_$w"}~);
$HTML =~ s~(\n|\r|\t)~~g;
if ($HTML !~ /html/i || $HTML !~ /body/i || $HTML !~ /ono/i) {
$abort = 0;
}
}
}
}

$DATA .= ONO::Cron::CronSync::ToolBox->print("Job '$data{'title'}': Run Status = $run, Abort Status = $abort",$switches);

if ($run && !$abort) {

ONO::IO->store("$CRON_DIR/lastrun/job_$job.txt",$timestamp);

################################################################
# cronsync modules - start
################################################################

if ($data{'type'} =~ /^ono_/) {
my ($DATA2,$MAIL2) = ONO::Cron::CronSync::ONO -> run($CRON_DIR,$job,\%data,\%TYPE,$vars_ref,$vpath,$sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$timestamp,$switches);
$DATA .= $DATA2;
$MAIL .= $MAIL2;
}

if ($data{'type'} =~ /^bot_/) {
my ($DATA2,$MAIL2) = ONO::Cron::CronSync::Bot -> run($CRON_DIR,$job,\%data,\%TYPE,$vars_ref,$vpath,$sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$timestamp,$switches);
$DATA .= $DATA2;
$MAIL .= $MAIL2;
}

if ($data{'type'} =~ /^web_/) {
my ($DATA2,$MAIL2) = ONO::Cron::CronSync::Web -> run($CRON_DIR,$job,\%data,\%TYPE,$vars_ref,$vpath,$sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$timestamp,$switches);
$DATA .= $DATA2;
$MAIL .= $MAIL2;
}

if ($data{'type'} =~ /^user_/) {
my ($DATA2,$MAIL2) = ONO::Cron::CronSync::Users -> run($CRON_DIR,$job,\%data,\%TYPE,$vars_ref,$vpath,$sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$timestamp,$switches);
$DATA .= $DATA2;
$MAIL .= $MAIL2;
}

if ($data{'type'} =~ /^dbms_/) {
my ($DATA2,$MAIL2) = ONO::Cron::CronSync::DBMS -> run($CRON_DIR,$job,\%data,\%TYPE,$vars_ref,$vpath,$sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$timestamp,$switches);
$DATA .= $DATA2;
$MAIL .= $MAIL2;
}

if ($data{'type'} =~ /^bak_/) {
my ($DATA2,$MAIL2) = ONO::Cron::CronSync::Backup -> run($CRON_DIR,$job,\%data,\%TYPE,$vars_ref,$vpath,$sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$timestamp,$switches);
$DATA .= $DATA2;
$MAIL .= $MAIL2;
}

if ($data{'type'} =~ /^svr_/) {
my ($DATA2,$MAIL2) = ONO::Cron::CronSync::FileServer -> run($CRON_DIR,$job,\%data,\%TYPE,$vars_ref,$vpath,$sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$timestamp,$switches);
$DATA .= $DATA2;
$MAIL .= $MAIL2;
}

if ($data{'type'} =~ /^apache_/) {
my ($DATA2,$MAIL2) = ONO::Cron::CronSync::Apache -> run($CRON_DIR,$job,\%data,\%TYPE,$vars_ref,$vpath,$sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$timestamp,$switches,$apache_trigger_mode);
$DATA .= $DATA2;
$MAIL .= $MAIL2;
}

if ($data{'type'} eq "monitor" || $data{'type'} =~ /^monitor_/) {
my ($DATA2,$MAIL2) = ONO::Cron::CronSync::Monitor -> run($CRON_DIR,$job,\%data,\%TYPE,$vars_ref,$vpath,$sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$timestamp,$switches);
$DATA .= $DATA2;
$MAIL .= $MAIL2;
}

if ($data{'type'} =~ /^os_/) {
my ($DATA2,$MAIL2) = ONO::Cron::CronSync::OS -> run($CRON_DIR,$job,\%data,\%TYPE,$vars_ref,$vpath,$sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$timestamp,$switches);
$DATA .= $DATA2;
$MAIL .= $MAIL2;
}

################################################################
# cronsync modules - end
################################################################

if ($data{'report_email'} =~ m~^(.*?)\@(.*?)\.(.*?)$~) {

if ($MAIL) {

$MAIL .= "\n###\n\n";

}

ONO::ToolBox::SendMail->sendmail(
"mail:noreply",
"$data{'report_email'}",
"[ONO] CronSync job report for $data{'title'}",
"$MAIL$DATA\nJob '$data{'title'}': COMPLETED\n\n",
);
}
}
} else {
my $TIME = "y:$data{'year'}/m:$data{'mon'}/d:$data{'mday'}/h:$data{'hour'}/m:$data{'min'}/w:$data{'wday'}/o:$data{'option'}";
$DATA .= ONO::Cron::CronSync::ToolBox->print("Job '$data{'title'}': No type or timeframe [$TIME] doesn't match - ABORT",$switches);
}
}

$DATA .= ONO::Cron::CronSync::ToolBox->print("Job '$data{'title'}': COMPLETED",$switches);

}
}

$DATA .= ONO::Cron::CronSync::ToolBox->print("ONO CronSync finished",$switches);
$DATA .= ONO::Cron::CronSync::ToolBox->print("BYE!",$switches);

ONO::IO->store("$CRON_DIR/report.txt",$DATA);

}

###############################################################################
# end of script
###############################################################################

1;

__END__