License Audit

Description of software-audit.pl


#!/usr/bin/perl -w

#
# submit-license-audit.pl -- Audit all the rush license file Valid: dates
#
# Name          Vers Date       Comments
# Greg Ercolano 1.00 08/06/08 - Initial implementation (submit-audit.pl)
# Greg Ercolano 1.10 09/23/13 - Modified to audit rush license.dat files
#
# (C) Copyright 2008, 2016 Seriss Corporation. All Rights Reserved.
# Do not remove this header or copyright notice. You may use and change
# this software for internal use, but may not re-distribute it.
# If you make changes, please add your name/version/date to the list
# above using the format shown.
#

use strict;
$|=1;
umask(0);

Start of program.

Here we enforce perl's strict syntax checking, make sure output is unbuffered ($|=1), and set the umask to zero, to prevent unix permission problems for the files we create.


### MODIFIABLE VARIABLES: START ####

# Use forward slashes in pathnames
$G::logdir     = "//eagle/net/tmp/logs";      		# where to write rush error logs
$G::cpus       = '+any=10.1@1';				# cpus to be audited

### MODIFIABLE VARIABLES: END ####
Change these variables to be the appropriate paths on your file server.
$G::logdir is the directory that will contain the Rush frame logs.
$G::cpus is the rush cpus specification for the machines the job will use.

$G::audit_file = "$G::logdir/rush-license-audit.txt";	# audit log file
@G::search     = ( "Licenses:", "Valid:", "Version:" );
$G::audit_file sets the pathname of the generated audit file. Here we set it to be in the job's log directory.

$G::search contains the list of strings to search for in the license.dat file. The value of the lines that match will be appended to the audit log.


# SUBMIT
if ( ! defined($ARGV[0]) ) {
    $G::self = $0; $G::self =~ s%\\%/%g;                # \\foo\bar -> //foo/bar

    # PREVENT 'NETWORK WORM' STYLE RECURSION
    if ( defined($ENV{RUSH_ISDAEMON}) ) { exit(1); }

    # CREATE LOGDIR
    if ( ! -d $G::logdir ) {
        unless(mkdir($G::logdir, 0777)) {
            print STDERR "mkdir $G::logdir: $!\n"; exit(1);
        }
    }

    # CREATE EMPTY AUDIT FILE
    unless(open(AUDIT, ">$G::audit_file"))
        { print STDERR "ERROR: $G::audit_file: $!\n"; exit(1); }
    print AUDIT "*** RUSH LICENSE AUDIT ***\n\n";

    # CREATE COLUMN HEADINGS
    print AUDIT sprintf("%-22s ", "Hostname");
    foreach my $search ( @G::search ) { print AUDIT sprintf("%-22s ", $search);  } print AUDIT "\n";

    # DOTTED LINE HEADING
    print AUDIT sprintf("%-22s ", ("-"x22));
    foreach my $search ( @G::search ) { print AUDIT sprintf("%-22s ", ("-"x22)); } print AUDIT "\n";
    close(AUDIT);

    # SUBMIT JOB
    unless( open(SUBMIT, "|rush -submit") )
        { print STDERR "rush -submit: $!\n"; exit(1); }
    print SUBMIT <<"EOF";
    title       RUSH_LICENSE_AUDIT
    ram         1
    frames      1-1000
    logdir      $G::logdir
    command     perl $G::self -render
    cpus        $G::cpus
    imgcommand  perl $G::self -imgcommand
EOF
    close(SUBMIT);

    # Non-zero exit indicates submit failed
    if ( $? >> 8 ) 
        { print STDERR "--- Submit failed\n"; exit(1); }      # Submit Failed
    exit(0);                                                  # Submit OK
}
This section submits the job.

It handles creating the log directory if it doesn't exist, clearing out the audit log's previous data by truncating it, and writing new column headers at the top.

Later on, when the script runs on the machines, they will append their findings to the audit file in whatever order machines become available.


# RETURN THE REQUESTED LICENSE INFO
#     $1 - string to search for
#
sub GetLicenseInfo($) {
    my $search = $_[0];
    my $licfile = "";
    if ( -e "c:/rush/etc/license.dat" ) { $licfile = "c:/rush/etc/license.dat"; }
    else                                { $licfile = "/usr/local/rush/etc/license.dat"; }

    unless(open(LIC,"<$licfile")) {
        print "ERROR: can't open $licfile: $!\n"; exit(1);
    }
    my $out = "";
    while () {
        if ( /$search/ ) { $out = $_; $out =~ s/.*${search}[ ]*//g; }
    }
    close(LIC);
    $out =~ s/^#[ ]*//;
    $out =~ s/[\r\n]*//g;
    chomp($out);
    return($out);
}
This section defines the function that opens the rush license.dat file and parses out the line to be searched for.

# RUNNING ON REMOTE MACHINE?
if ( $ARGV[0] eq "-render" ) {
    print "--- Working on frame host $ENV{RUSH_HOSTNAME}\n";

    # REMOVE THIS HOST FROM JOB (SO IT DOESN'T RUN AGAIN)
    system("rush -an $ENV{RUSH_HOSTNAME} -fu");

    # BUILD AN AUDIT LINE FOR THIS MACHINE
    my $out = sprintf("%-22.22s ", $ENV{RUSH_HOSTNAME});
    foreach my $search ( @G::search ) { $out .= sprintf("%-22s ", GetLicenseInfo($search)); } $out .= "\n";

    # APPEND LINE TO AUDIT FILE WITH SINGLE "ATOMIC WRITE"
    print "Appending to ${G::audit_file}: $out";
    unless(open(AUDIT, ">>$G::audit_file"))
        { print STDERR "ERROR: $G::audit_file: $!\n"; exit(1); }
    else
        { syswrite(AUDIT, $out, length($out)); close(AUDIT); }
    print "\n--- DONE\n";
    exit(0);
}
This is the section that runs on each machine.

The first thing it does is add this machine's hostname to the job's 'neverhost' list so that this machine doesn't try to run the script again. (We only want to run once on each machine)

Next, the script builds a line of info that will be appended to audit log, and appends it using a single atomic write operation, which should ensure the data doesn't get "intermingled" with data being written concurrently by other machines.


# USER MIDDLE CLICKED ON A FRAME IN IRUSH? (IMGCOMMAND)
if ( $ARGV[0] eq "-imgcommand" ) {
    my $cmd;
    if ( -d "c:/rush/etc" ) {
        # WINDOWS
        $ENV{PATH} = "c:/program files/windows nt/accessories:$ENV{PATH}";
	$G::audit_file =~ s%/%\\%g;
	$cmd = "start wordpad $G::audit_file";
    } elsif ( -d "/Applications" ) {
        # MAC
	$cmd = "open $G::audit_file";
    } else {
        # LINUX
        $cmd = "gvim $G::audit_file";
	$cmd =~ s%\\%/%g;
    }
    system($cmd);
    exit(0);
}
This last section is invoked by irush if someone middle-clicks on one of the job's frames, and handles opening the audit log in a GUI text editor, using techniques specific to the different platforms. (Windows, Mac, Linux..)

This behavior is set up by the 'imgcommand' line in the 'SUBMIT' section of this script.