From 85cebec74a7b4a9b7f7fc0938de17c838a80662f Mon Sep 17 00:00:00 2001 From: tobiasly Date: Fri, 13 Dec 2002 05:46:08 +0000 Subject: [PATCH] Initial add of specialized Config modules. Some parts are not fully implemented. --- lib/BackupPC/Config.pm | 358 ++++++++++++++++++++++++++ lib/BackupPC/Config/Db.pm | 435 ++++++++++++++++++++++++++++++++ lib/BackupPC/Config/Db/MySQL.pm | 111 ++++++++ lib/BackupPC/Config/Text.pm | 198 +++++++++++++++ 4 files changed, 1102 insertions(+) create mode 100644 lib/BackupPC/Config.pm create mode 100644 lib/BackupPC/Config/Db.pm create mode 100644 lib/BackupPC/Config/Db/MySQL.pm create mode 100644 lib/BackupPC/Config/Text.pm diff --git a/lib/BackupPC/Config.pm b/lib/BackupPC/Config.pm new file mode 100644 index 0000000..26ccead --- /dev/null +++ b/lib/BackupPC/Config.pm @@ -0,0 +1,358 @@ +package BackupPC::Config; + +use warnings; +use Data::Dumper; + +our %ConfigDef; + +# this currently isn't used (or completed) +sub CheckConfigInfo +{ + my($self) = @_; + my $errstr = ''; + + my($attr, $val, $def, $ref); + + foreach $attr (sort keys %{ $self->{Conf} }) { + print AA "Checking $attr..."; + $val = $self->{Conf}->{$attr}; + $ref = ref $val; + $def = $ConfigDef{$attr}; + + if (!defined $def) { + $errstr .= "Unknown attribute $attr; "; + } elsif ($def->{struct} eq 'SCALAR' && $ref) { + $errstr .= "$attr expected to be SCALAR but is $ref; "; + } elsif ($def->{struct} =~ /^ARRAY(OFHASH)$/ && $ref && $ref ne 'ARRAY') { + $errstr .= "$attr expected to be ARRAY but is $ref; "; + } elsif ($def->{struct} =~ /^HASH(OFARRAY)$/ && $ref && $ref ne 'HASH') { + $errstr .= "$attr expected to be HASH but is $ref; "; + # still working out this logic.. + #} elsif (defined $val && !$ref) { + # # if we got a scalar but were expecting a reference, fix it + # + # if($def->{struct} eq 'ARRAY') { + # $val = [ $val ]; + # } elsif ($def->{struct} eq 'HASH') { + # $val = { $val }; + # } elsif ($def->{struct} eq 'ARRAYOFHASH') { + # $val = [ { $val } ]; + # } elsif ($def->{struct} eq 'HASHOFARRAY') { + # $val = { [ $val ] }; + # } + + } + } + + return $errstr; +} + +sub TopDir +{ + my($self) = @_; + return $self->{TopDir}; +} + +sub BinDir +{ + my($self) = @_; + return $self->{BinDir}; +} + +sub Version +{ + my($self) = @_; + return $self->{Version}; +} + +sub Conf +{ + my($self) = @_; + return %{$self->{Conf}}; +} + +sub ConfigDef +{ + my($self) = @_; + return \%ConfigDef; +} + +sub Lang +{ + my($self) = @_; + return $self->{Lang}; +} + +sub adminJob +{ + return " admin "; +} + +sub trashJob +{ + return " trashClean "; +} + +sub timeStamp +{ + my($self, $t, $noPad) = @_; + my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) + = localtime($t || time); + $year += 1900; + $mon++; + return "$year/$mon/$mday " . sprintf("%02d:%02d:%02d", $hour, $min, $sec) + . ($noPad ? "" : " "); +} + +sub ConnectData { + # fallback routine in case no database used + return 1; + +} + +########################### + + +%ConfigDef = ( + ServerHost => {struct => 'SCALAR', + type => 'STRING', }, + + ServerPort => {struct => 'SCALAR', + type => 'INT', }, + + ServerMesgSecret => {struct => 'SCALAR', + type => 'STRING', }, + + MyPath => {struct => 'SCALAR', + type => 'STRING', }, + + UmaskMode => {struct => 'SCALAR', + type => 'INT', }, + + WakeupSchedule => {struct => 'ARRAY', + type => 'INT', }, + + MaxBackups => {struct => 'SCALAR', + type => 'INT', }, + + MaxUserBackups => {struct => 'SCALAR', + type => 'INT', }, + + MaxPendingCmds => {struct => 'SCALAR', + type => 'INT', }, + + MaxOldLogFiles => {struct => 'SCALAR', + type => 'INT', }, + + DfPath => {struct => 'SCALAR', + type => 'STRING', }, + + DfMaxUsagePct => {struct => 'SCALAR', + type => 'INT', }, + + TrashCleanSleepSec => {struct => 'SCALAR', + type => 'INT', }, + + DHCPAddressRanges => {struct => 'ARRAYOFHASH', + type => {ipAddrBase => 'STRING', + first => 'INT', + last => 'INT',}, }, + + BackupPCUser => {struct => 'SCALAR', + type => 'STRING', }, + + CgiDir => {struct => 'SCALAR', + type => 'STRING', }, + + InstallDir => {struct => 'SCALAR', + type => 'STRING', }, + + BackupPCUserVerify => {struct => 'SCALAR', + type => 'BOOLEAN', }, + + SmbShareName => {struct => 'ARRAY', + type => 'STRING', }, + + SmbShareUserName => {struct => 'SCALAR', + type => 'STRING', }, + + SmbSharePasswd => {struct => 'SCALAR', + type => 'STRING', }, + + TarShareName => {struct => 'ARRAY', + type => 'STRING', }, + + FullPeriod => {struct => 'SCALAR', + type => 'FLOAT', }, + + IncrPeriod => {struct => 'SCALAR', + type => 'FLOAT', }, + + FullKeepCnt => {struct => 'SCALAR', + type => 'INT', }, + + FullKeepCntMin => {struct => 'SCALAR', + type => 'INT', }, + + FullAgeMax => {struct => 'SCALAR', + type => 'INT', }, + + IncrKeepCnt => {struct => 'SCALAR', + type => 'INT', }, + + IncrKeepCntMin => {struct => 'SCALAR', + type => 'INT', }, + + IncrAgeMax => {struct => 'SCALAR', + type => 'INT', }, + + IncrFill => {struct => 'SCALAR', + type => 'BOOLEAN', }, + + RestoreInfoKeepCnt => {struct => 'SCALAR', + type => 'INT', }, + + BackupFilesOnly => {struct => 'HASHOFARRAY', + type => 'STRING', }, + + BackupFilesExclude => {struct => 'HASHOFARRAY', + type => 'STRING', }, + + BlackoutBadPingLimit => {struct => 'SCALAR', + type => 'INT', }, + + BlackoutGoodCnt => {struct => 'SCALAR', + type => 'INT', }, + + BlackoutHourBegin => {struct => 'SCALAR', + type => 'FLOAT', }, + + BlackoutHourEnd => {struct => 'SCALAR', + type => 'FLOAT', }, + + BlackoutWeekDays => {struct => 'ARRAY', + type => 'INT', }, + + XferMethod => {struct => 'SCALAR', + type => 'STRING', }, + + SmbClientPath => {struct => 'SCALAR', + type => 'STRING', }, + + SmbClientArgs => {struct => 'SCALAR', + type => 'STRING', }, + + TarClientCmd => {struct => 'SCALAR', + type => 'STRING', }, + + TarFullArgs => {struct => 'SCALAR', + type => 'STRING', }, + + TarIncrArgs => {struct => 'SCALAR', + type => 'STRING', }, + + TarClientRestoreCmd => {struct => 'SCALAR', + type => 'STRING', }, + + TarClientPath => {struct => 'SCALAR', + type => 'STRING', }, + + SshPath => {struct => 'SCALAR', + type => 'STRING', }, + + NmbLookupPath => {struct => 'SCALAR', + type => 'STRING', }, + + FixedIPNetBiosNameCheck => {struct => 'SCALAR', + type => 'BOOLEAN', }, + + PingPath => {struct => 'SCALAR', + type => 'STRING', }, + + PingArgs => {struct => 'SCALAR', + type => 'STRING', }, + + CompressLevel => {struct => 'SCALAR', + type => 'INT', }, + + PingMaxMsec => {struct => 'SCALAR', + type => 'INT', }, + + SmbClientTimeout => {struct => 'SCALAR', + type => 'INT', }, + + MaxOldPerPCLogFiles => {struct => 'SCALAR', + type => 'INT', }, + + SendmailPath => {struct => 'SCALAR', + type => 'STRING', }, + + EMailNotifyMinDays => {struct => 'SCALAR', + type => 'INT', }, + + EMailFromUserName => {struct => 'SCALAR', + type => 'STRING', }, + + EMailAdminUserName => {struct => 'SCALAR', + type => 'STRING', }, + + EMailNoBackupEverMesg => {struct => 'SCALAR', + type => 'MEMO', }, + + EMailNotifyOldBackupDays => {struct => 'SCALAR', + type => 'INT', }, + + EMailNoBackupRecentMesg => {struct => 'SCALAR', + type => 'MEMO', }, + + EMailNotifyOldOutlookDays => {struct => 'SCALAR', + type => 'INT', }, + + EMailOutlookBackupMesg => {struct => 'SCALAR', + type => 'MEMO', }, + + CgiAdminUserGroup => {struct => 'SCALAR', + type => 'STRING', }, + + CgiAdminUsers => {struct => 'SCALAR', + type => 'STRING', }, + + Language => {struct => 'SCALAR', + type => 'STRING', }, + + CgiUserHomePageCheck => {struct => 'SCALAR', + type => 'STRING', }, + + CgiUserUrlCreate => {struct => 'SCALAR', + type => 'STRING', }, + + CgiDateFormatMMDD => {struct => 'SCALAR', + type => 'BOOLEAN', }, + + CgiNavBarAdminAllHosts => {struct => 'SCALAR', + type => 'BOOLEAN', }, + + CgiHeaderFontType => {struct => 'SCALAR', + type => 'STRING', }, + + CgiHeaderFontSize => {struct => 'SCALAR', + type => 'INT', }, + + CgiNavBarBgColor => {struct => 'SCALAR', + type => 'STRING', }, + + CgiHeaderBgColor => {struct => 'SCALAR', + type => 'STRING', }, + + CgiHeaders => {struct => 'SCALAR', + type => 'STRING', }, + + CgiImageDir => {struct => 'SCALAR', + type => 'STRING', }, + + CgiImageDirURL => {struct => 'SCALAR', + type => 'STRING', }, + +); + +1; diff --git a/lib/BackupPC/Config/Db.pm b/lib/BackupPC/Config/Db.pm new file mode 100644 index 0000000..5343fa6 --- /dev/null +++ b/lib/BackupPC/Config/Db.pm @@ -0,0 +1,435 @@ +package BackupPC::Config::Db; + +use base 'BackupPC::Config'; +use warnings; +use strict; + +use DBI; +our $SELF; + +sub BackupInfoRead +{ + my($self, $client) = @_; + + # ORDER BY is important! BackupPC_dump expects list to be sorted + my $cmd = "SELECT " . join(', ', @{ $self->{BackupFields} }) + . " FROM Backup WHERE client = '$client' ORDER BY num"; + my $sth = $self->{dbh}->prepare($cmd); + + $sth->execute; + my($row, @backups); + +NUM: + while ($row = $sth->fetchrow_hashref) { + $backups[@backups] = { %$row }; + } + + return @backups; +} + +sub BackupInfoWrite +{ + my($self, $client, @backups) = @_; + + #BackupPC_dump passes an array containing all backup records, so we must + #1) figure out which ones aren't in the database and add them; then + #2) delete records in the database that weren't passed + + # get a hash of currently existing backup nums from database + my %current = map {$_, 1} + @{ $self->{dbh}->selectcol_arrayref("SELECT num FROM Backup") }; + + my %textFields = map {$_, 1} 'client', @{ $self->{BackupTextFields} }; + + my($num, $cmd, $sth); + +NUM: + foreach my $backup (@backups) { + $num = $backup->{num}; + + if (defined $current{$num}) { + #it's in the database as well as @backups; delete it from hash + delete $current{$num}; + + } else { + #it's not in database yet, so add it + $cmd = "INSERT Backup (client, " . join(', ', @{ $self->{BackupFields} }) + . ") VALUES ('$client', " . join(', ', + map {(defined $textFields{$_})? "'$backup->{$_}'" : $backup->{$_}} + @{ $self->{BackupFields} }) . ")"; + + $self->{dbh}->prepare($cmd)->execute; + } + + } + + # any remaining items in %current should be discarded + if (%current) { + $cmd = "DELETE FROM Backup WHERE num IN (" . join(', ', sort keys %current) + . ")"; + $self->{dbh}->prepare($cmd)->execute; + } + +} + + +# See comments in "Backup" subs, above +sub RestoreInfoRead +{ + my($self, $client) = @_; + + # ORDER BY is important! BackupPC_dump expects list to be sorted + my $cmd = "SELECT " . join(', ', @{ $self->{RestoreFields} }) + . " FROM Restore WHERE client = '$client' ORDER BY num"; + my $sth = $self->{dbh}->prepare($cmd); + + $sth->execute; + my($row, @restores); + +NUM: + while ($row = $sth->fetchrow_hashref) { + $restores[@restores] = { %$row }; + } + + return @restores; +} + + +# See comments in "Backup" subs, above +sub RestoreInfoWrite +{ + my($self, $client, @restores) = @_; + + my %current = map {$_, 1} + @{ $self->{dbh}->selectcol_arrayref("SELECT num FROM Restore") }; + + my %textFields = map {$_, 1} 'client', @{ $self->{RestoreTextFields} }; + + my($num, $cmd, $sth); + +NUM: + foreach my $restore (@restores) { + $num = $restore->{num}; + + if (defined $current{$num}) { + delete $current{$num}; + + } else { + $cmd = "INSERT Restore (client, " . join(', ', @{ $self->{RestoreFields} }) + . ") VALUES ('$client', " . join(', ', + map {(defined $textFields{$_})? "'$restore->{$_}'" : $restore->{$_}} + @{ $self->{RestoreFields} }) . ")"; + + $self->{dbh}->prepare($cmd)->execute; + } + + } + + if (%current) { + $cmd = "DELETE FROM Restore WHERE num IN (" . join(', ', sort keys %current) + . ")"; + $self->{dbh}->prepare($cmd)->execute; + } + +} + +sub HostInfoRead { + my($self, $oneClient) = @_; + + my $cmd = "SELECT client AS host, dhcp, user, moreUsers FROM Client"; + my $sth = $self->{dbh}->prepare($cmd); + + $sth->execute; + my($row, $client, %clients); + +CLIENT: + while ($row = $sth->fetchrow_hashref) { + $client = $row->{host}; + + if (defined $oneClient) { + next CLIENT unless $oneClient eq $client; + $clients{$client} = {%$row}; + return \%clients; + } + + $clients{$client} = {%$row}; + } + + return \%clients; + +} + + +#TODO: Replace w/ Db version!! +sub ConfigRead +{ + my($self, $host) = @_; + my($ret, $mesg, $config, @configs); + + our %Conf; + + $self->{Conf} = (); + push(@configs, "$self->{TopDir}/conf/config.pl"); + push(@configs, "$self->{TopDir}/pc/$host/config.pl") + if ( defined($host) && -f "$self->{TopDir}/pc/$host/config.pl" ); + foreach $config ( @configs ) { + %Conf = (); + if ( !defined($ret = do $config) && ($! || $@) ) { + $mesg = "Couldn't open $config: $!" if ( $! ); + $mesg = "Couldn't execute $config: $@" if ( $@ ); + $mesg =~ s/[\n\r]+//; + return $mesg; + } + %{$self->{Conf}} = ( %{$self->{Conf} || {}}, %Conf ); + } + + #$mesg = $self->CheckConfigInfo; + #return $mesg if $mesg; + + return if ( !defined($self->{Conf}{Language}) ); + + my $langFile = "$self->{LibDir}/BackupPC/Lang/$self->{Conf}{Language}.pm"; + + if ( !defined($ret = do $langFile) && ($! || $@) ) { + $mesg = "Couldn't open language file $langFile: $!" if ( $! ); + $mesg = "Couldn't execute language file $langFile: $@" if ( $@ ); + $mesg =~ s/[\n\r]+//; + return $mesg; + } + + our %Lang; + $self->{Lang} = \%Lang; + + return; +} + +our %gConfigWriteHandler = (SCALAR => \&_ConfigWriteScalar, + ARRAY => \&_ConfigWriteArray, + HASH => \&_ConfigWriteHash, + ARRAYOFHASH => \&_ConfigWriteArrayOfHash, + HASHOFARRAY => \&_ConfigWriteHashOfArray, + ); + +our %gConfigTypeField; # will be defined by database-specific Config module + +sub ConfigWrite { + my($self, $client) = @_; + $SELF = $self; + my $dbh = $self->{dbh}; + + $dbh->{RaiseError} = 0; + my($cmd, $sth); + + $cmd = "DELETE FROM Config WHERE client = '~~$client'"; + $sth = $dbh->prepare($cmd) or return "$cmd\n". $dbh->errstr; + $sth->execute or return "$cmd\n". $dbh->errstr; + + $cmd = "UPDATE Config SET client = '~~$client' WHERE client = '$client'"; + $sth = $dbh->prepare($cmd) or return "$cmd\n". $dbh->errstr; + $sth->execute or return "$cmd\n". $dbh->errstr; + + my($attr, $val, $def, $handler, $mesg); + + foreach $attr (sort keys %{ $self->{Conf} }) { + $val = $self->{Conf}->{$attr}; + $def = $self->{ConfigDef}->{$attr}; + + $handler = $gConfigWriteHandler{$def->{struct}}; + $mesg = &$handler($dbh, $def, $client, $attr, $val); + return $mesg if $mesg; + } + + + $cmd = "DELETE FROM Config WHERE client = '~~$client'"; + $sth = $dbh->prepare($cmd) or return "$cmd\n". $dbh->errstr; + $sth->execute or return "$cmd\n". $dbh->errstr; + + $self->{dbh}->{RaiseError} = 1; + + return; +} + +sub _ConfigWriteScalar { + my($dbh, $def, $client, $attr, $val) = @_; + $SELF->Debug("SCALAR $val") if $attr eq 'BackupFilesOnly'; + return if !defined $val; + + my $ref = ref $val; + + if ($ref) { + return "Expected $attr to be SCALAR, but got $ref"; + } + + &_WriteConfigRow($dbh, $client, $attr, -1, '', $def->{type}, $val) +} + +sub _ConfigWriteArray { + my($dbh, $def, $client, $attr, $val, $key) = @_; + $SELF->Debug("ARRAY $val, $key") if $attr eq 'BackupFilesOnly'; + return if !defined $val; + + $key = '' unless defined $key; + my $ref = ref $val; + + if (!$ref) { + #expecting ARRAY, got string -- implicit convert + $val = [ $val ]; + } elsif ($ref ne 'ARRAY') { + $attr = "$attr\{$key}" if $key ne ''; + return "Expected $attr to be ARRAY, but got $ref"; + } + + my $subscript = 0; + my $item; + my $type = $def->{type}; + + foreach $item (@$val) { + &_WriteConfigRow($dbh, $client, $attr, $subscript++, + $key, $type, $item) + } +} + +sub _ConfigWriteHash { + my($dbh, $def, $client, $attr, $val, $subscript) = @_; + $SELF->Debug("HASH $val") if $attr eq 'BackupFilesOnly'; + return if !defined $val; + + $subscript = -1 unless defined $subscript; + my $ref = ref $val; + + if (!$ref) { + #expecting HASH, got string -- implicit convert + $val = { '*' => $val }; + } elsif ($ref ne 'HASH') { + $attr = "$attr\[$subscript]" if $subscript != -1; + return "Expected $attr to be HASH, but got $ref"; + } + + my($key, $item); + my $type = $def->{type}; + + # If 'type' is a hash ref, this means the attribute's subvalue type + # depends on what its corresponding key is. In that case, we set + # $thisType for each iteration; otherwise, we leave it set to 'type' + my $typeByKey = ref $type; + my $thisType = $type; + + foreach $key (sort keys %$val) { + $item = $val->{$key}; + + if ($typeByKey) { + $thisType = $type->{$key}; + + if (!defined $thisType) { + return "Don't know how to handle subvalue $key for $attr"; + } + } + + &_WriteConfigRow($dbh, $client, $attr, $subscript, + $key, $thisType, $item) + } +} + +sub _ConfigWriteArrayOfHash { + my($dbh, $def, $client, $attr, $val) = @_; + return if !defined $val; + + my $ref = ref $val; + + if (!$ref) { + #expecting ARRAY, got string -- implicit convert + $val = [ $val ]; + } elsif ($ref ne 'ARRAY') { + return "Expected $attr to be ARRAY, but got $ref"; + } + + my $subscript = 0; + my $item; + + foreach $item (@$val) { + &_ConfigWriteHash($dbh, $def, $client, $attr, + $item, $subscript++); + } +} + +sub _ConfigWriteHashOfArray { + my($dbh, $def, $client, $attr, $val) = @_; + $SELF->Debug("HASHOFARRAY $val") if $attr eq 'BackupFilesOnly'; + return if !defined $val; + + my $ref = ref $val; + + if (!$ref) { + #expecting HASH, got string -- implicit convert + $val = { '*' => $val }; + } elsif ($ref ne 'HASH') { + return "Expected $attr to be HASH, but got $ref"; + } + + my($key, $item); + + foreach $key (sort keys %$val) { + $item = $val->{$key}; + &_ConfigWriteArray($dbh, $def, $client, $attr, + $item, $key); + } + +} + +sub _WriteConfigRow { + my($dbh, $client, $attr, $subscript, $key, $type, $val) = @_; + + defined $gConfigTypeField{$type} + or return "Unknown ConfigDef type '$type' ($attr); aborting"; + + my($confType, $field, %fields); + + while(($confType, $field) = each %gConfigTypeField) { + if ($confType eq $type) { + # this is the correct field for value of interest, + # so copy and format it + $fields{$field} = ($confType =~ /^(STRING|MEMO)$/)? + $dbh->quote($val) : $val; + } else { + $fields{$field} = 'NULL'; + } + } + + $fields{'client'} = $dbh->quote($client); + $fields{'clientGroup'} = "''"; #TODO: add group logic + $fields{'attribute'} = $dbh->quote($attr); + $fields{'subscript'} = $subscript; + $fields{'hashKey'} = $dbh->quote($key); + + my @fields = sort keys %fields; + my @values = map { $fields{ $_ } } @fields; + + my $cmd = "INSERT Config (" . join(', ', @fields) . ")\nVALUES (" + . join(', ', @values) . ")"; + + my $sth = $dbh->prepare($cmd) or return "$cmd\n\n" . $dbh->errstr; + $sth->execute or return "$cmd\n\n". $dbh->errstr; + + return; +} + +#TODO: Replace w/ Db version!! +# +# Return the mtime of the config file +# +sub ConfigMTime +{ + my($self) = @_; + return (stat("$self->{TopDir}/conf/config.pl"))[9]; +} + + + +sub DESTROY { + my($self) = @_; + + $self->{dbh}->disconnect if defined $self->{dbh}; +} + + +1; diff --git a/lib/BackupPC/Config/Db/MySQL.pm b/lib/BackupPC/Config/Db/MySQL.pm new file mode 100644 index 0000000..dfb21cb --- /dev/null +++ b/lib/BackupPC/Config/Db/MySQL.pm @@ -0,0 +1,111 @@ +package BackupPC::Config::Db::MySQL; + +use base 'BackupPC::Config::Db'; +use warnings; +use strict; + +use DBI; +use DBD::mysql; + +%BackupPC::Config::Db::gConfigTypeField = + (BOOLEAN => 'valueBit', + INT => 'valueInt', + FLOAT => 'valueFloat', + STRING => 'valueString', + MEMO => 'valueMemo', + ); + +sub ConnectData { + my($self) = @_; + + my $dsn = 'DBI:mysql:database=backuppc;host=reagan'; + $self->{dbh} = DBI->connect($dsn, 'root', undef, {RaiseError => 1, + AutoCommit => 1,}); +} + +sub ConfigMTime { + my($self) = @_; +return time(); + my $cmd = "SHOW TABLE STATUS LIKE 'Config'"; + my $sth = $self->{dbh}->prepare($cmd); + + $sth->execute; + my $row = $sth->fetchrow_hashref || return time(); + my $mtime; + + if (defined($mtime = $row->{'Update_time'})) { + $mtime =~ m/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/ + || return time(); + return &Date_SecsSince1970($2,$3,$1,$4,$5,$6); + } else { + return time(); + } +} + +sub HostsMTime { + my($self) = @_; +return time(); + my $cmd = "SHOW TABLE STATUS LIKE 'Client'"; + my $sth = $self->{dbh}->prepare($cmd); + + $sth->execute; + my $row = $sth->fetchrow_hashref || return time(); + my $mtime; + + if (defined($mtime = $row->{'Update_time'})) { + return &Date_SecsSince1970($mtime); + } else { + return time(); + } +} + +# Date subs borrowed from Date::Manip. +# Copyright (c) 1995-2001 Sullivan Beck. All rights reserved. +# This program is free software; you can redistribute it and/or modify it +# under the same terms as Perl itself. + +sub Date_SecsSince1970 { + my($mysqlDate) = @_; + $mysqlDate =~ m/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/ + || return time(); + my($y,$m,$d,$h,$mn,$s) = ($1,$2,$3,$4,$5,$6); + my($sec_now,$sec_70,$Ny,$N4,$N100,$N400,$dayofyear,$days)=(); + my($cc,$yy)=(); + + $y=~ /(\d{2})(\d{2})/; + ($cc,$yy)=($1,$2); + + $Ny=$y; + + $N4=($Ny-1)/4 + 1; + $N4=0 if ($y==0); + + $N100=$cc + 1; + $N100-- if ($yy==0); + $N100=0 if ($y==0); + + $N400=($N100-1)/4 + 1; + $N400=0 if ($y==0); + + my(@days) = ( 0, 31, 59, 90,120,151,181,212,243,273,304,334,365); + my($ly)=0; + $ly=1 if ($m>2 && &Date_LeapYear($y)); + + $dayofyear=$days[$m-1]+$d+$ly; + $days= $Ny*365 + $N4 - $N100 + $N400 + $dayofyear; + $sec_now=($days-1)*24*3600 + $h*3600 + $mn*60 + $s; + $sec_70 =62167219200; + return ($sec_now-$sec_70); +} + +sub Date_LeapYear { + my($y)=@_; + return 0 if $y % 4; + return 1 if $y % 100; + return 0 if $y % 400; + return 1; +} + + + +1; diff --git a/lib/BackupPC/Config/Text.pm b/lib/BackupPC/Config/Text.pm new file mode 100644 index 0000000..8ee2927 --- /dev/null +++ b/lib/BackupPC/Config/Text.pm @@ -0,0 +1,198 @@ +package BackupPC::Config::Text; + +use warnings; +use strict; +use Fcntl qw/:flock/; + +use base 'BackupPC::Config'; + +sub BackupInfoRead +{ + my($self, $host) = @_; + local(*BK_INFO, *LOCK); + my(@Backups); + + flock(LOCK, LOCK_EX) if open(LOCK, "$self->{TopDir}/pc/$host/LOCK"); + if ( open(BK_INFO, "$self->{TopDir}/pc/$host/backups") ) { + while ( ) { + s/[\n\r]+//; + next if ( !/^(\d+\t(incr|full)[\d\t]*$)/ ); + $_ = $1; + @{$Backups[@Backups]}{@{$self->{BackupFields}}} = split(/\t/); + } + close(BK_INFO); + } + close(LOCK); + return @Backups; +} + +sub BackupInfoWrite +{ + my($self, $host, @Backups) = @_; + local(*BK_INFO, *LOCK); + my($i); + + flock(LOCK, LOCK_EX) if open(LOCK, "$self->{TopDir}/pc/$host/LOCK"); + unlink("$self->{TopDir}/pc/$host/backups.old") + if ( -f "$self->{TopDir}/pc/$host/backups.old" ); + rename("$self->{TopDir}/pc/$host/backups", + "$self->{TopDir}/pc/$host/backups.old") + if ( -f "$self->{TopDir}/pc/$host/backups" ); + if ( open(BK_INFO, ">$self->{TopDir}/pc/$host/backups") ) { + for ( $i = 0 ; $i < @Backups ; $i++ ) { + my %b = %{$Backups[$i]}; + printf(BK_INFO "%s\n", join("\t", @b{@{$self->{BackupFields}}})); + } + close(BK_INFO); + } + close(LOCK); +} + +sub RestoreInfoRead +{ + my($self, $host) = @_; + local(*RESTORE_INFO, *LOCK); + my(@Restores); + + flock(LOCK, LOCK_EX) if open(LOCK, "$self->{TopDir}/pc/$host/LOCK"); + if ( open(RESTORE_INFO, "$self->{TopDir}/pc/$host/restores") ) { + while ( ) { + s/[\n\r]+//; + next if ( !/^(\d+.*)/ ); + $_ = $1; + @{$Restores[@Restores]}{@{$self->{RestoreFields}}} = split(/\t/); + } + close(RESTORE_INFO); + } + close(LOCK); + return @Restores; +} + +sub RestoreInfoWrite +{ + my($self, $host, @Restores) = @_; + local(*RESTORE_INFO, *LOCK); + my($i); + + flock(LOCK, LOCK_EX) if open(LOCK, "$self->{TopDir}/pc/$host/LOCK"); + unlink("$self->{TopDir}/pc/$host/restores.old") + if ( -f "$self->{TopDir}/pc/$host/restores.old" ); + rename("$self->{TopDir}/pc/$host/restores", + "$self->{TopDir}/pc/$host/restores.old") + if ( -f "$self->{TopDir}/pc/$host/restores" ); + if ( open(RESTORE_INFO, ">$self->{TopDir}/pc/$host/restores") ) { + for ( $i = 0 ; $i < @Restores ; $i++ ) { + my %b = %{$Restores[$i]}; + printf(RESTORE_INFO "%s\n", + join("\t", @b{@{$self->{RestoreFields}}})); + } + close(RESTORE_INFO); + } + close(LOCK); +} + +sub ConfigRead +{ + my($self, $host) = @_; + my($ret, $mesg, $config, @configs); + + our %Conf; + + $self->{Conf} = (); + push(@configs, "$self->{TopDir}/conf/config.pl"); + push(@configs, "$self->{TopDir}/pc/$host/config.pl") + if ( defined($host) && -f "$self->{TopDir}/pc/$host/config.pl" ); + foreach $config ( @configs ) { + %Conf = (); + if ( !defined($ret = do $config) && ($! || $@) ) { + $mesg = "Couldn't open $config: $!" if ( $! ); + $mesg = "Couldn't execute $config: $@" if ( $@ ); + $mesg =~ s/[\n\r]+//; + return $mesg; + } + %{$self->{Conf}} = ( %{$self->{Conf} || {}}, %Conf ); + } + + #$mesg = $self->CheckConfigInfo; + #return $mesg if $mesg; + + return if ( !defined($self->{Conf}{Language}) ); + + my $langFile = "$self->{LibDir}/BackupPC/Lang/$self->{Conf}{Language}.pm"; + + if ( !defined($ret = do $langFile) && ($! || $@) ) { + $mesg = "Couldn't open language file $langFile: $!" if ( $! ); + $mesg = "Couldn't execute language file $langFile: $@" if ( $@ ); + $mesg =~ s/[\n\r]+//; + return $mesg; + } + + our %Lang; + $self->{Lang} = \%Lang; + + return; +} + + +# +# Return the mtime of the config file +# +sub ConfigMTime +{ + my($self) = @_; + return (stat("$self->{TopDir}/conf/config.pl"))[9]; +} + +# +# Returns information from the host file in $self->{TopDir}/conf/hosts. +# With no argument a ref to a hash of hosts is returned. Each +# hash contains fields as specified in the hosts file. With an +# argument a ref to a single hash is returned with information +# for just that host. +# +sub HostInfoRead +{ + my($self, $host) = @_; + my(%hosts, @hdr, @fld); + local(*HOST_INFO); + + if ( !open(HOST_INFO, "$self->{TopDir}/conf/hosts") ) { + print(STDERR $self->timeStamp, + "Can't open $self->{TopDir}/conf/hosts\n"); + return {}; + } + while ( ) { + s/[\n\r]+//; + s/#.*//; + s/\s+$//; + next if ( /^\s*$/ || !/^([\w\.-]+\s+.*)/ ); + @fld = split(/\s+/, $1); + if ( @hdr ) { + if ( defined($host) ) { + next if ( lc($fld[0]) ne $host ); + @{$hosts{lc($fld[0])}}{@hdr} = @fld; + close(HOST_INFO); + return \%hosts; + } else { + @{$hosts{lc($fld[0])}}{@hdr} = @fld; + } + } else { + @hdr = @fld; + } + } + close(HOST_INFO); + return \%hosts; +} + +# +# Return the mtime of the hosts file +# +sub HostsMTime +{ + my($self) = @_; + return (stat("$self->{TopDir}/conf/hosts"))[9]; +} + + + +1; -- 2.20.1