#!/dataserfs/libs-2024-01-11/bin/perl -w

use strict;

delete @ENV{'PATH','IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
$ENV{'PATH'} = '/bin:/usr/bin';

use DBI;
use File::FcntlLock;
use Carp::Assert;

my $lockfile	= "/var/run/ddu-scand.lock";
my $logfile	= "/var/log/ddu-scand.log";

my $TIMEOUT = 12 * 3600;	# 12 hours

my @MysqlOptions = qw(  DBI:MariaDB:
                        mariadb_server_prepare=0
                        mariadb_read_default_file=/etc/my.cnf );

my $DDU	= "/home/dataserfs/ddu/ddu-client";

my %CF = (
	'Cc'		=> 'runbookdev@microsoft.com',
	'Return-Path'	=> 'sigcompute@microsoft.com',
	'DefaultEmailFrom' => 'DDU Scan',
);


# #####################################################################
sub CloseDB{ my( $dbh ) = @_;
# #####################################################################
	assert($dbh);
        $dbh->disconnect();
} # CloseDB



# #####################################################################
# $port is optional
sub OpenDB{my($host,$database,$db_user,$db_pass, $port) = @_;
# #####################################################################
	assert($host);
	assert($database);
	assert($db_user);
	assert($db_pass);
	# port may be NULL

my $dsn = join(';', @MysqlOptions, "host=$host", "database=$database");
        if ( defined $port ) {
                $dsn .= ";port=$port";          # add port if defined
        } # if

        my $dbh = DBI->connect($dsn, $db_user, $db_pass,
        {
            RaiseError => 1,
            AutoCommit => 1,
            PrintError => 0,
        });

 return($dbh);
} # OpenDB
# #####################################################################



# #####################################################################
sub logmsg{my($msg) = @_;
# #####################################################################
	print STDERR  scalar localtime(), " ", $msg, "\n";
} # logmsg







# ######################################################################
sub sendmail{ my ($recipient, $msg, $subject, $parmopts ) = @_;
# ######################################################################
	assert($recipient);
	assert($msg);
	assert($subject);
my %opts;
#
# set some defaults
#
        if (defined $CF{Cc}){
                $opts{'Cc'} = $CF{Cc};
        }

        if (defined $CF{EmailReturnPath}){
                $opts{'Return-Path'} = $CF{EmailReturnPath};
        }

        if (defined $CF{DefaultEmailFrom}) {
                $opts{'From'} = $CF{DefaultEmailFrom};
        } # if

        if ( defined $parmopts ) {
                while( my($k,$v) = each( %{$parmopts} ) ) {
                        $opts{$k} = $v;
                }

        } # if

#print STDERR scalar localtime, " sendmail $recipient, $subject\n";
#print STDERR "$msg\n\n";

        open PIPE, "|/usr/lib/sendmail -t -oi -oem" ||
                                                die( "cannot open pipe: $!");
        print PIPE "Subject: $subject\n";
        print PIPE "To: $recipient\n";
        #
        # add in other opts
        #
        while ( my ( $k, $v ) = each( %opts ) ) {
                print PIPE "$k: $v\n";
        } # while

        #####print PIPE "\n";
        print PIPE "$msg\n";
        # we must not die here, or emails will be resend the next time...
        close(PIPE);
} # sendmail












# #####################################################################
sub runjob{my($scanno,$root,$uid,$scan_req_user) = @_;
# #####################################################################

	if( (!$scanno) or (!defined $uid) or (!$root) or (!$scan_req_user)) {
		logmsg("bad job: scanno:$scanno, uid: $uid, root:$root, user;$scan_req_user");
		return;
	} # if

	my $scan_user = getpwuid($uid);
	if ( (!defined $scan_user) or $scan_user eq "" ) {
		logmsg( "bad scan_user for uid $uid: '$scan_user'");
		return;
	}

	#
	# ok, proceed
	#

	logmsg("new job: scanno:$scanno, root:$root, as user: $scan_user, requested by: $scan_req_user");

	# the logfile will be created as $uid, or may not exist if su fails
	# TODO: what if the user is root?
	#
	my $logfile = "/tmp/ddu-scand-$scanno";
	unlink($logfile);
	`/bin/su $scan_user -c "cd $root && /usr/bin/timeout $TIMEOUT $DDU >$logfile 2>&1"`;
my $ok = ( $? == 0 );
my $output = "?";

 	if(open(IN,$logfile)){
		local $/ = undef;
		$output = <IN>;
		close(IN);
	} else {
	} # else

my $recipient 	= "$scan_req_user\@microsoft.com";

	if( $ok ) {
		logmsg("end job OK: scanno:$scanno\n [$output]");
		my $msg = qq{
			Dear $scan_req_user,

			Directory $root has been scanned.
			See https://runbook.svceng.com/login/ddu-scan.cgi

			cheers,
			FileScan
		};
		sendmail($recipient, $msg, "ddu scan $root");
	} else {
		logmsg("end job ERROR: scanno:$scanno\n [$output]");
		my $msg = qq{
			Dear $scan_req_user,

			Directory scan $root has failed.

			$output

			sorry,
			FileScan
		};

		sendmail($recipient, $msg, "failed: ddu scan $root");
	} # else

} # runjob





















# #####################################################################
sub main_thing{
# #####################################################################

	open(STDOUT,">>$logfile") || die( "logfile $!");
	open(STDERR,">>$logfile") || die( "logfile $!");

	# lock to make sure we run only once
	# open for append ( i.e. create if needed )
	open(LOCK, ">>$lockfile" ) || die( "lockfile $!");
	my $fs = new File::FcntlLock( l_type => F_WRLCK);
	if ( ! defined $fs->lock(\*LOCK, F_SETLK) ) {
		#unable to lock
		exit (0) if ( $fs->error =~ /already locked/ );
		die("unable to lock: $!, " . $fs->error);
	} # if


my $db  	= "ddu";
my $db_host  	= "localhost";
my $db_user  	= "ddu";
my $db_pw  	= "ddu";
my $dbh = OpenDB($db_host,$db,$db_user,$db_pw) ||
		die("cannot connect to db '$db' on '$db_host' as '$db_user'");


	while(1) {
		my($scanno,
		   $root,
		   $uid,
		   $scan_req_utc,
		   $scan_req_user) = $dbh->selectrow_array(qq{
			SELECT	scanno,root,uid,
				scan_req_utc,scan_req_user
			FROM	scans
			WHERE	scan_req_utc IS NOT NULL
			ORDER BY scan_req_utc
			LIMIT 1
			});

		if( $dbh->err ) {
			die($dbh->errstr);
		}

		if( $scanno ) {
			# job found

			runjob($scanno,$root,$uid,$scan_req_user);

			$dbh->do(qq{
			UPDATE	scans
			SET	scan_req_utc = NULL
			where	scanno = ?
			}, undef, $scanno) || die( $dbh->errstr);
		} else {
			sleep(2);
		} #
	} # while

	CloseDB($dbh);
	close(LOCK);
} # main_thing



main_thing(@ARGV);
