#!/usr/bin/perl -w
# 
# ***** BEGIN LICENSE BLOCK *****
# Zimbra Collaboration Suite, Network Edition.
# Copyright (C) 2014, 2016 Synacor, Inc.  All Rights Reserved.
# ***** END LICENSE BLOCK *****
# 

use strict;

$ENV{'LD_LIBRARY_PATH'} = '/opt/zimbra/lib';
my $PNAME = 'vmware-heartbeat';
my $PID_FILE = "/opt/zimbra/log/$PNAME.pid";
my $LOG_FILE = "/opt/zimbra/log/$PNAME.log";
my $CMD = '/opt/zimbra/libexec/vmware-appmonitor';

sub getPid() {
    if (-e $PID_FILE) {
        if (open(FH, "< $PID_FILE")) {
            my $pid = <FH>;
            if (defined($pid)) {
                chomp($pid);
            }
            close(FH);
            return $pid;
        } else {
            print STDERR "Unable to read pid file $PID_FILE: $!\n";
            exit(1);
        }
    }
    return 0;
}

sub enable() {
    my $out = `$CMD enable 2>&1`;
    my $rc = $? >> 8;
    if ($rc != 0) {
        print STDERR "Unable to enable app monitoring (rc=$rc):\n$out\n";
        return 0;
    }
    return 1;
}

sub disable() {
    my $out = `$CMD disable 2>&1`;
    my $rc = $? >> 8;
    if ($rc != 0) {
        print STDERR "Unable to disable app monitoring (rc=$rc):\n$out\n";
        return 0;
    }
    return 1;
}

sub isEnabled() {
    my $retval = 0;
    if (open(FH, "$CMD isEnabled 2> /dev/null |")) {
        my $line;
        while (defined($line = <FH>)) {
            chomp($line);
            if (lc($line) eq 'true') {
                $retval = 1;
                last;
            } elsif (lc($line) eq 'false') {
                last;
            }
        }
        close(FH);
    } else {
        print STDERR "Unable to check if app monitoring is enabled: $!\n";
    }
    return $retval;
}

sub sendHeartbeat() {
    my $out = `$CMD markActive 2>&1`;
    my $rc = $? >> 8;
    if ($rc != 0) {
        print STDERR "Unable to send heartbeat (rc=$rc):\n$out\n";
    }
}

sub isZcsHealthy() {
    my $rc = system('zmcontrol status > /dev/null');
    $rc >>= 8;
    if ($rc == 0) {
        return 1;  # healthy
    } else {
        return 0;  # unhealthy
    }
}

sub usage() {
    print <<_USAGE_;
Usage: $PNAME <start|stop> [interval]
interval = number of seconds between heartbeats; default 15
_USAGE_
    exit(1);
}


#
# main
#

my $cmd = $ARGV[0] || '';
my $interval = $ARGV[1] || 15;
$interval = $interval + 0;
if ($interval <= 0) {
    usage();
}

if ($cmd eq 'start') {
    enable();

    my $pid = getPid();
    if ($pid > 0 && kill(0, $pid)) {
        print STDERR "Already running! (pid=$pid)\n";
        exit(0);
    }

    my $pidFork = fork();
    if (!defined($pid)) {
        print STDERR "Unable to fork!\n";
        exit(1);
    } elsif ($pidFork != 0) {
        # parent process
        exit(0);
    } else {
        # child process
        open(STDOUT, ">> $LOG_FILE");
        open(STDERR, ">> $LOG_FILE");
        if (open(FH, "> $PID_FILE")) {
            print FH "$$\n";
            close(FH);
        } else {
            print STDERR "Unable to create pid file $PID_FILE: $!\n";
            exit(1);
        }
        while (1) {
            my $t;
            if (isEnabled()) {
                $t = localtime();
                print "$t: Checking ZCS status\n";
# 
# Bug: 76780
#
# If zmcontrol does not return a status to isZcsHealthy fast enough, delayed heartbeat signal to VMware HA
# monitoring (via sendHeartbeat()) can cause VMware HA to initiate a failover.
#                if (1) {
#                ^ This (instead of the next line of code) prevents erroneous system failure reports, BUT
#                  ALWAYS reports the system as healthy.
#
                if (isZcsHealthy()) {
                    $t = localtime();
                    print "$t: Sending heartbeat to VM\n";
                    sendHeartbeat();
                }
            } else {
                $t = localtime();
                print "$t: App monitoring is not enabled\n";
            }
            sleep($interval);
        }
    }
} elsif ($cmd eq 'stop') {
    disable();

    my $pid = getPid();
    if ($pid > 0 && kill(0, $pid)) {
        kill('TERM', $pid);
    }
    if (-e $PID_FILE && !unlink($PID_FILE)) {
        print STDERR "Unable to delete pid file $PID_FILE: $!\n";
    }
} else {
    usage();
}

exit(0);

