package Razor2::Client::Config;
use strict;
use Data::Dumper;
use vars qw( $VERSION );
use File::Copy;
use Razor2::Logger;
sub new {
my ($class) = @_;
return bless {}, $class;
}
sub read_conf {
my ($self,$params) = @_;
my $default_conf_fn = "$self->{global_razorhome}/razor-agent.conf";
my $conf;
my $defaults = $self->default_agent_conf();
if ($self->{razorconf}) {
$conf = $self->read_file($self->{razorconf},$defaults)
unless ($self->{opt}->{create} && $self->{opt}->{config});
$self->find_home($conf->{razorhome});
} else {
$self->compute_razorconf();
if ($self->{razorconf}) {
$conf = $self->read_file($self->{razorconf},$defaults);
} else {
$self->log(6, "No razor-agent.conf found, using defaults. ");
$conf = $defaults;
}
}
foreach (keys %{$self->{opt}}) {
next if ($_ eq '');
$conf->{$_} = $self->{opt}->{$_};
}
if ($params) {
foreach (keys %$params) {
$conf->{$_} = $params->{$_};
}
}
$self->{conf} = $conf;
$^W = 0 unless $conf->{debug};
if ($self->{razorhome}) {
foreach (qw( logfile pidfile listfile_catalogue listfile_nomination
listfile_discovery whitelist identity)) {
next unless $conf->{$_};
next if $conf->{$_} =~ /^\//;
$conf->{$_} = "$self->{razorhome}/$conf->{$_}";
}
}
return $self->{conf};
}
sub compute_razorconf {
my $self = shift;
my $default_conf_fn = "$self->{global_razorhome}/razor-agent.conf";
$self->{razorconf} = "";
$self->find_home();
if ($self->{razorhome}) {
my $mycf = "$self->{razorhome}/razor-agent.conf";
$self->{computed_razorconf} = $mycf;
if (-r $mycf) {
$self->{razorconf} = $mycf;
} elsif (-e $mycf) {
$self->log(5, "Found but can't read $mycf, skipping.");
} else {
$self->log(5, "No $mycf found, skipping.");
}
}
if (!$self->{razorconf} && -e $default_conf_fn) {
if (-r $default_conf_fn) {
$self->{razorconf} = $default_conf_fn;
} else {
$self->log(5, "Found but can't read $default_conf_fn, skipping.");
$self->{computed_razorconf} ||= $default_conf_fn;
}
}
}
sub write_conf {
my ($self,$hash) = @_;
unless ($self->{razorconf}) {
$self->log(5,"Cannot write_conf without razorconf set");
return $self->error("Cannot write_conf without razorconf set");
}
my $now = localtime();
my $srcmsg;
unless ($hash) {
$hash = $self->default_agent_conf();
if (-r $self->{razorconf}) {
$hash = $self->read_file( $self->{razorconf}, $hash);
$srcmsg = "Non-default values taken from $self->{razorconf}";
} else {
$srcmsg = "Created with all default values";
}
}
my $clientheader = <<EOFCLIENT;
EOFCLIENT
return $self->write_file($self->{razorconf}, $hash, 0, $clientheader);
}
sub find_user {
my $self = shift;
return 1 if $self->{user};
$self->{user} = getpwuid($>) || do {
$self->log(1, "Can't figure out who the effective user is: $!");
return undef;
};
return 1;
}
sub find_home {
my ($self,$rhome) = @_;
my $dotrazor = '.razor';
$dotrazor = '_razor' if $^O eq 'VMS';
if (defined $self->{razorhome}) {
$self->{razorhome_computed} = $self->{razorhome};
return 1;
}
$rhome ||= $self->{opt}->{razorhome};
unless ($rhome) {
if (defined $ENV{HOME}) {
$rhome = File::Spec->catdir("$ENV{HOME}", "$dotrazor");
} else {
return unless $self->find_user();
$rhome = File::Spec->catdir((getpwnam($self->{user}))[7], "$dotrazor") || "/home/$self->{user}/$dotrazor";
}
$rhome = VMS::Filespec::unixify($rhome) if $^O eq 'VMS';
$self->log(8,"Computed razorhome from env: $rhome");
}
$self->{razorhome_computed} = $rhome;
if (-d $rhome) {
if (-w $rhome) {
$self->log(6,"Found razorhome: $rhome");
} else {
$self->log(6,"Found razorhome: $rhome, however, can't write to it.");
}
$self->{razorhome} = $rhome;
return 1;
}
if ($self->{razorconf}) {
my $path = $$self{razorconf};
if ($path =~ m:/:) {
if ($path =~ m:(.*)/:) {
$self->{razorhome} = $1;
return 1;
}
}
}
$self->log(5,"No razorhome found, using all defaults");
$self->{razorhome} = "";
return 1;
}
sub create_home {
my ($self,$rhome) = @_;
if (-d $rhome) {
$self->{razorhome} = $rhome;
return 1;
}
if (mkdir $rhome, 0755) {
$self->log(6,"Created razorhome: $rhome");
$self->{razorhome} = $rhome;
return 1;
}
return $self->error("Could not mkdir $rhome: $!");
}
sub compute_identity {
my ($self) = @_;
$self->find_home() or return;
return 1 if $self->{identity};
my $id;
if ($id = $self->{opt}->{identity}) {
$self->{identity} = $self->my_readlink($id);
$self->log(6,"Cant read identity: $self->{identity}")
unless ($self->{opt}->{register}) || (-r $self->{identity});
return 1;
} elsif ($id = $self->{conf}->{identity}) {
$self->{identity} = $self->my_readlink($id);
return 1;
} else {
$id = $self->{razorhome} ? "$self->{razorhome}/identity" : "";
$self->{identity} = $self->my_readlink($id);
return 1;
}
}
sub get_ident {
my ($self) = @_;
$self->find_home() or return;
my $idfn = $self->{identity};
return $self->error("Cannot read the identity file: $idfn") unless -r $idfn;
$idfn = $self->my_readlink($idfn);
my $mode = ((stat($idfn))[2]) & 07777; if ($mode & 0007) {
$self->log(2,"Please chmod $idfn so it is not world readable.");
}
return $self->read_file( $idfn );
}
sub save_ident {
my ($self,$ident) = @_;
$self->find_home() or return;
my $orig;
my $syml;
my $obase = "identity-$ident->{user}";
if ($orig = $self->{opt}->{identity}) {
} else {
$orig = "$self->{razorhome}/$obase";
$syml = "$self->{razorhome}/identity";
}
rename($orig,"$orig.bak") if -s $orig;
my $umask = umask 0077; $self->write_file($orig,$ident) or return;
umask $umask;
return $orig unless $syml;
unless ($self->{opt}->{symlink}) {
return $orig if -e $syml; }
unlink $syml if -e $syml;
if (eval { symlink("",""); 1 } ) {
symlink $obase, $syml or
return $self->error("Created $orig, but could not symlink to it $syml: $!");
} else {
$self->log(5, "symlinks dont work on this machine");
copy($orig,$syml);
}
return $orig;
}
sub my_readlink {
my ($self,$fn) = @_;
while (1) {
return $fn unless -l $fn;
if ($fn =~ /^(.*)\/([^\/]+)$/) {
my $dir = $1;
$fn = readlink $fn;
$fn = $1 if $fn =~ /^(\S+)$/; $fn = "$dir/$fn" unless $fn =~ /^\//;
} else {
$fn = readlink $fn;
$fn = $1 if $fn =~ /^(\S+)$/; }
}
}
sub parse_value {
my ($self, $value) = @_;
$value =~ s/^\s+//;
$value =~ s/\s+$//;
if ($value =~ m:,:) {
my @values = split /,\s*/, $value;
return [@values];
} else {
return $value;
}
}
sub read_file {
my ($self,$fn,$h,$nothash) = @_;
my $conf = ref($h) eq 'HASH' ? $h : {};
if( $^O eq 'VMS' && $fn !~ /\[/ ) {
my ($dir,$file,$ext) = ($fn =~ /(^.*\/)(.*)(\..*)$/);
$dir =~ s/\./_/g;
$file =~ s/\./_/g;
$fn = $dir . $file . $ext;
}
unless (($fn =~ /^\//) || -e $fn) {
$self->log(7,"Can't read file $fn, looking relatve to $self->{razorhome}");
$fn = "$self->{razorhome}/$fn";
}
my $total = 0;
my @lines;
unless (open CONF, "<$fn") {
$self->log(5,"Can't read file $fn: $!");
return;
}
for (<CONF>) {
chomp;
next if /^\s* if ($nothash) {
next unless s/^\s*(.+?)\s*$/$1/; $conf->{$_} = 7;
push @lines, $_;
} else {
next unless /=/;
my ($attribute, $value) = /^\s*(.+?)\s*=\s*(.+?)\s*$/; next unless (defined $attribute && defined $value);
$conf->{$attribute} = $self->parse_value($value);
}
$total++;
}
close CONF;
$self->log(5, "read_file: $total items read from $fn");
return $nothash ? \@lines : $conf;
}
sub write_file {
my ($self,$fn,$hash,$append,$header,$lock) = @_;
$fn = "$self->{razorhome}/$fn" unless ($fn =~ /^\//);
$fn = ">$fn" if $append;
if( $^O eq 'VMS' && $fn !~ /\[/ ) {
my ($dir,$file,$ext) = ($fn =~ /(^.*\/)(.*)(\..*)$/);
$dir =~ s/\./_/g;
$file =~ s/\./_/g;
$fn = $dir . $file . $ext;
}
my $lockfile = "$fn.lock";
$lockfile = "${fn}_lock;1" if $^O eq 'VMS';
if ($lock) {
if (-r "$lockfile") {
return $self->error("File is locked, try again later: $lockfile");
} else {
unless (open LOCK, ">$fn.lock") {
return $self->error("Can't create lock file $fn.lock: $!");
}
close LOCK;
}
}
unless (open CONF, ">$fn") {
return $self->error("Can't write file $fn: $!");
}
print CONF "$header\n" if $header;
my $total = 0;
if (ref($hash) eq 'HASH') {
foreach (sort keys %$hash) {
return $self->error("Key cannot contain '=': $_") if /=/;
printf CONF "%-22s = ", $_;
if (ref($hash->{$_}) eq "ARRAY") {
print CONF join(',', @{$hash->{$_}}) ."\n";
} else {
print CONF $hash->{$_} ."\n";
}
$total++;
}
} elsif (ref($hash) eq 'ARRAY') {
foreach (@$hash) {
next unless /\S/;
if (ref($_) eq "ARRAY") {
print CONF join(', ', @$_) ."\n";
} else {
print CONF $_ ."\n";
}
$total++;
}
} elsif (ref($hash) eq 'SCALAR') {
printf CONF $$hash;
$total++;
}
close CONF;
if ($lock) {
1 while unlink "$lockfile";
}
$self->log(5, "wrote $total ". ref($hash) ." items to file: $fn");
return 1;
}
sub default_server_conf {
my $self = shift;
my $defaults = {
srl => -1,
ep4 => '7542-10',
bql => 4,
ac => 0,
bqs => 128,
se => '0F', dre => 4,
zone => 'razor2.cloudmark.com',
logic_method => 4,
};
foreach (keys %$defaults) {
$defaults->{$_} = $self->parse_value($defaults->{$_});
}
return $defaults;
}
sub default_agent_conf {
my $self = shift;
my $defaults = {
debuglevel => "3",
logfile => "razor-agent.log",
listfile_catalogue => "servers.catalogue.lst",
listfile_nomination => "servers.nomination.lst",
listfile_discovery => "servers.discovery.lst",
min_cf => "ac",
turn_off_discovery => "0",
ignorelist => "0",
razorzone => "razor2.cloudmark.com",
rediscovery_wait => "172800",
report_headers => "1",
whitelist => "razor-whitelist",
use_engines => "1, 2, 3, 4",
identity => "identity",
logic_method => 4,
sort_by_distance => 0,
};
foreach (keys %$defaults) {
$defaults->{$_} = $self->parse_value($defaults->{$_});
}
return $defaults;
}
1;