Commit 88fdc7f0 authored by Leigh B. Stoller's avatar Leigh B. Stoller

Ah yes, I can waste time like the best of the best. Actually, I'm just

waiting for Mike to work on Jail. This is an auditing module. Its
intended to serve two purposes. 1) Provide a common set of routines
for generating all that audit email from various scripts and 2)
provide a debugging hook for when things screw up via the web
interface and the user is too clueless to help us out, or the
information just got lost someplace.

The main function is:

	AuditStart($daemonize;$logname);

To start an audit, call AuditStart(). The first arg indicates if the caller
is wanting to daemonize. If not, just redirect stdout/stderr to a logfile,
and return. The logfile is optional; if not provided one will be created
based on the name of the script with mktemp. If the user wants to
daemonize, also fork and detach. This is provided as a convenience for
those scripts that tend to combine redirecting output and daemonizing. The
parent is exptected to exit, like any good mother of a daemon.

Okay, so all output is redirected to the log file. If a subscript is
invoked that also calls audit, that call is ignored, under the assumption
that the logging can be rolled into the parent, and besides it would create
a blizzard of email.

There is a package destructor (END) that is setup to email the log file to
the audit list, and if a log was created, the log goes to the logs
list. This is important. The audit list never gets any logs; it just gets a
two line record of what was done ("rmacct mike (by stoller)"). The log goes
separately to the logs list for inspection if needed at some point. This
makes the audit list very consise and easy to distill.

Like all good package destructors, you can tell if the script was
exiting with an error. If it was, then instead of sending the mail to
the logs list, send the message to tbops! The nice thing is that this
gets invoked no matter how you exit! No need to explicitly send the
email, unless of course you want it, but I have not written than
function yet!

Oh, for debugging. We can go stick in Audit calls when scripts
misbehave, and we can watch the output.

Cool, right? Really useful, right? Handy Dandy, right? Mike?
parent 2dbf3722
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2003 University of Utah and the Flux Group.
# All rights reserved.
#
package libaudit;
use Exporter;
@ISA = "Exporter";
@EXPORT =
qw ( AuditStart AuditEnd );
# After package decl.
use English;
use POSIX qw(isatty setsid);
use File::Basename;
use IO::Handle;
#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use libtestbed;
my $TBOPS = "@TBOPSEMAIL@";
my $TBAUDIT = "@TBAUDITEMAIL@";
my $TBLOGS = "@TBLOGSEMAIL@";
my $SCRIPTNAME = "Unknown";
my $USERNAME = "Unknown";
my @SAVEARGV = @ARGV;
my $SAVEPID = $PID;
# Indicates, this script is being audited.
my $auditing = 0;
# Where the log is going. When not defined, do not send it in email!
my $logfile;
# Untainted scriptname for email below.
if ($PROGRAM_NAME =~ /^([-\w\.\/]+)$/) {
$SCRIPTNAME = basename($1);
}
else {
$SCRIPTNAME = "Tainted";
}
# The user running the script.
if (my ($name) = getpwuid($UID)) {
$USERNAME = $name;
}
#
# Start an audit (or log) of a script. First arg is a flag indicating if
# the script should fork/detach. The second (optional) arg is a file name
# into which the log should be written. The return value is non-zero in the
# parent, and zero in the child (if detaching).
#
sub AuditStart($;$)
{
my($daemon, $logname) = @_;
#
# If we are already auditing, then do not audit a child script. This
# would result in a blizzard of email! We wrote the scripts, so we
# should now what they do!
#
if (defined($ENV{'TBAUDITON'})) {
return;
}
#
# If this is an interactive session, then do not bother with a log
# file. Just send it to the output and hope the user is smart enough to
# save it off. We still want to audit the operation though, sending a
# "what was done" message to the audit list, and CC it to tbops if it
# exits with an error. But the log is the responsibility of the user.
#
if (!$daemon && isatty(STDIN)) {
$auditing = 1;
$ENV{'TBAUDITON'} = "$SCRIPTNAME:$USERNAME";
return;
}
if (!defined($logname)) {
$logfile = TBMakeLogname("$SCRIPTNAME");
}
else {
$logfile = $logname;
}
$ENV{'TBAUDITLOG'} = $logfile;
$ENV{'TBAUDITON'} = "$SCRIPTNAME:$USERNAME";
#
# Okay, daemonize.
#
if ($daemon) {
my $mypid = fork();
if ($mypid) {
select(undef, undef, undef, 0.2);
return $mypid;
}
#
# Create a new session to ensure we are clear of any process group
#
setsid() or
die("setsid failed: $!");
#
# We have to disconnect from the caller by redirecting both STDIN
# and STDOUT away from the pipe. Otherwise the caller (the web
# server) will continue to wait even though the parent has exited.
#
open(STDIN, "< /dev/null") or
die("opening /dev/null for STDIN: $!");
}
$auditing = 1;
#
# If setuid, lets reset the owner/mode of the log file. Otherwise its
# owned by root, mode 600 and a pain to deal with later, especially if
# the script drops its privs!
#
if ($UID != $EUID) {
chown($UID, $EUID, $logfile);
chmod(0664, $logfile);
}
open(STDOUT, ">> $logfile") or
die("opening $logfile for STDOUT: $!");
open(STDERR, ">> $logfile") or
die("opening $logfile for STDERR: $!");
#
# Turn off line buffering on output
#
STDOUT->autoflush(1);
STDERR->autoflush(1);
return 0;
}
#
# Finish an Audit.
#
sub AuditEnd()
{
SendAuditMail(0);
}
#
# Internal function to send the email. First argument is exit status.
#
# Two messages are sent. A topical message is sent to the audit list. This
# is a short message that says what was done and by who. The actual log of
# what happened is sent to the logs list so that we can go back and see the
# details if needed.
#
sub SendAuditMail($)
{
my($exitstatus) = @_;
if ($auditing) {
# Avoid duplicate messages.
$auditing = 0;
my $subject = "$SCRIPTNAME @SAVEARGV";
if ($?) {
$subject = "Failed: $subject";
}
my $body = "$SCRIPTNAME @SAVEARGV\n" . "Invoked by $USERNAME";
if ($?) {
$body .= "\nExited with status: $?";
}
SENDMAIL($TBAUDIT, $subject, $body, $USERNAME, undef, ());
# Success and no log ...
if ($exitstatus == 0 && !defined($logfile)) {
return;
}
#
# Send logfile to tblogs. Carbon to tbops if it failed. If no logfile
# then no point in sending to tblogs, obviously.
#
my $TO;
my $HDRS = "Reply-To: $TBOPS";
my @FILES = ();
if (defined($logfile)) {
$TO = $TBLOGS;
@FILES = ($logfile);
$HDRS .= "\nCC: $TBOPS" if ($?);
}
else {
$TO = $TBOPS;
}
SENDMAIL($TO, $subject, $body, $USERNAME, $HDRS, @FILES);
if (defined($logfile)) {
unlink($logfile);
}
}
}
#
# When the script ends, if the audit has not been sent, send it.
#
END {
# Save, since shell commands will alter it.
my $exitstatus = $?;
SendAuditMail($exitstatus);
$? = $exitstatus;
}
1;
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment