# -*-perl-*-

# $Id: Log.plm,v 1.8 2005/11/01 22:45:17 thomas Exp $

# The logging routines for sushi.

# $Log: Log.plm,v $
# Revision 1.8  2005/11/01 22:45:17  thomas
# DBI connect with user '' to use current krb5 ticket cache principal
#
# Revision 1.7  2005/10/03 19:46:58  thomas
# Always authenticate to postgres as the current krb5 default principal.
#
# Revision 1.6  2003/06/12 21:57:33  thomas
# final save before 0.3.11 branch
#
# Revision 1.5  2003/06/09 14:45:11  thomas
# schema version of database
#
# Revision 1.4  2002/07/23 16:02:27  thomas
# version 0.2.5
#
# Revision 1.3  2002/07/10 14:01:28  thomas
# sushi-0.2.4
#
# Revision 1.2  2002/07/08 15:56:33  thomas
# 0.2.3 release
#
# Revision 1.1.1.1  2002/05/21 14:13:25  thomas
# sushi - backup to disk
#

package Sushi::Log;

use strict;
use Sys::Hostname;
use Time::localtime;
use IPC::Open3;

my $syslog_initialized = 0;
my $logdbh = undef;

###############################################################################
# The constructor if a logging object is created directly

sub new {
  my $invocant = shift;
  my $class = ref ($invocant) || $invocant;
  my $logging_type = 'Normal';
  if ($0 =~ /(sushid|sushiremote)/ && $::debug<=3) {
    $logging_type = 'Database';
  }
  my $self = { debug => $::debug,
	       logging_type => $logging_type,
	       @_
	     };
  bless ($self, $class);
  return $self;
}


sub error {
  my $self = shift;

  $self->log (0, @_);
}

###############################################################################

sub log {
  # print @_;
  my ($self) = shift;
  my ($level) = shift;

  # Error checking...
  (ref($self) && defined ($self->{debug}) && defined ($level)) or return;

  if (!defined $self->{logging_type}) {
    $self->{logging_type} = 'Normal';
    if ($0 =~ /(sushid|sushiremote)/ && $::debug<=3) {
      $self->{logging_type} = 'Database';
    }
  }
  ($self->{debug} >= $level) or return;
  if ($self->{logging_type} eq 'Syslog') {
    &init_syslog();
    if ($level) {
      syslog ('debug', join ("", @_));
    } else {
      syslog ('err', join ("", @_));
    }
  } elsif ($self->{logging_type} eq 'Database') {

    defined ($logdbh) or $logdbh = DBI->connect
      ($CSL::DBs::sushi, '', '', { AutoCommit => 1 });

    defined ($logdbh) and $logdbh->do ("set search_path to sushi;");
    if (!defined $self->{logging_hostname}) {
      $self->{logging_hostname} = hostname;
    }
    if (!defined ($logdbh)) {
      print STDERR join ("", "$$:", @_);
      return;
    }
    chomp (my $line = join ("", @_));
    my $rows = $logdbh->do
      ("insert into backend_log (host, pid, timestamp, message) values (" .
       $logdbh->quote ($self->{logging_hostname}) . ", $$, " .
       $logdbh->quote (ctime (time)) . ", " .
       $logdbh->quote ($line) . ")");
  } else {
    print STDERR join ("", "$$:", @_);
  }
}

###############################################################################
# Log a message prepended with information from the passed atom.

sub logAtom {

  my ($self) = shift;
  my ($level) = shift;

  (ref ($self) && (ref ($self) =~ /Sushi::Atom/)) or
    return $self->log ($level, @_);

  # Error checking...
  (ref($self) && defined ($self->{debug}) && defined ($level)) or return;

  ($self->{debug} >= $level) or return;

  my $commonName = $self->commonName;

  if (!defined $self->{logging_type}) {
    $self->{logging_type} = 'Normal';
    if ($0 =~ /(sushid|sushiremote)/ && $::debug<=3) {
      $self->{logging_type} = 'Database';
    }
  }

  if ($self->{logging_type} eq 'Syslog') {
    &init_syslog();
    if ($level) {
      syslog ('debug', "$commonName: " . join ("", @_));
    } else {
      syslog ('err', "$commonName: " . join ("", @_));
    }
    return;
  }

  if ($self->{logging_type} eq 'Database') {

    defined ($logdbh) or $logdbh = DBI->connect
      ($CSL::DBs::sushi, '', '', { AutoCommit => 1 });

    if (!defined $self->{logging_hostname}) {
      $self->{logging_hostname} = hostname;
    }
    if (!defined ($logdbh)) {
      print STDERR "$commonName: $$: ", @_;
      return;
    }
    my $row = {};
    chomp ($row->{message} = join ("", @_));
    $row->{host} = $self->{logging_hostname};
    $row->{pid} = $$;
    $row->{schedule_group} = $self->{schedule_group};
    $row->{type} = $self->{type};
    $row->{name} = $self->{name};
    $row->{id} = $self->{id};
    $row->{timestamp} = time;

    # Fall through on failure
    Sushi::GenDB::add ($row, {}, $::table_defs->{backend_log}) and return;
  }

  # Normal logging
  print STDERR "$commonName: $$:", @_;
  return;
}

###############################################################################

sub resetDB {
  if (defined $logdbh) {
    $logdbh->disconnect;
    $logdbh = undef;
  }
}


###############################################################################

sub init_syslog {
  $syslog_initialized > 0 and return;
  openlog ('sushi', 'pid', 'daemon');
  $syslog_initialized = 1;
}


###############################################################################

# Get the default principal out of the current krb5 ticket cache
sub krb5_get_default_principal {

  my ($luser, $cin, $cout);
  my $pid = open3 ($cin, $cout, undef, '/s/sushi-0.3.19/depend/bin/klist');
  close $cin;
  if (defined $cout) {
    while (<$cout>) {
      # Take only the first component of the principal, since
      # that's all that postgres uses
      if (/^Default principal:\s+(\w+)(\@|\/)/) {
	$luser = $1;
	last;
      }
    }
    close $cout;
  }
  # Don't accumulate zombies
  waitpid $pid,0 if defined $pid;

  $luser;
}


###############################################################################

1;
