[Mimedefang] tie'd files

Jan Pieter Cornet johnpc at xs4all.nl
Sun Aug 31 17:54:49 EDT 2008


On Sat, Aug 30, 2008 at 09:37:52PM -0400, Tuc at T-B-O-H.NET wrote:
[...]
>   tie %mailid, "DB_File", "/etc/mail/mailid.db", O_RDONLY;
>   tie %variout, "DB_File", "/etc/mail/variout.db", O_RDONLY;
> 
>       $varilookup = $variout{$LookupRecipient};
> 
>       if ($varilookup) {
>         $changeto     = $varilookup;
>       }
> 
> 
> 	pretty normal stuff. BESIDES the fact that the /etc/mail
> maps are 640 (FreeBSD atleast) and owned by root/wheel, I'm running
> into a more important problem... 
> 
> 	If I update and remake the map, and then chmod it, the next
> time the subroutine runs, it gets the OLD data. I've tried assigning
> the tie to a variable, and then $variable->sync before and after any
> use, but that doesn't seem to be helping.

->sync is only meant to write out changed local data, not to pick up
changes in the external file.

The problem is: if there is an update, what usually happens is that a
completely new .db file is placed on top of the old one. If you don't
specifically untie and re-tie, your process will still have the old file
open, with the original data in it.

It's generally a bad idea to force BerkeleyDB files to do an in-place
update while readers have the database open: since readers tend to cache
certain parts of the database, this is almost guaranteed to blow up at
some point.

What we do is, before each email is checked (usually in filter begin),
we check if the file that underlies the map has been changed. If it is,
we untie and re-tie the map.

I'll attach the code that we use for this. It's probably easy to use,
but it's a bit obscured by the fact that the code that checks for a
changed file, is separated from the actual tieing and untieing code, so
it can be reused.

=head2 _chk_file_stat

(not exported). Checks if a file is changed, and if so, calls code
to reprocess the file.

Call as:

  _chk_file_stat($file, \&reread_sub);

$file is the filename to read

&reread_sub($file) is supposed to reread $file, the function should
return an open file handle (to call stat() on)

=cut

my %stat_info;

sub _chk_file_stat {
    my($file, $code) = @_;

    my $reread;
    if ( !exists $stat_info{$file} ) {
        $reread++;
    } else {
        my @filestat = stat($file)
            or die "Cannot stat $file: $!\n";
        if ( $filestat[1] != $stat_info{$file}{inode} or
             $filestat[9] != $stat_info{$file}{mtime} )
        {
            $reread++;
        }
    }

    return unless $reread;
    my $fh = $code->($file);
    my @dbstat = stat($fh);
    @dbstat or die "Cannot stat $file via handle $fh: $!\n";
    $stat_info{$file}{inode} = $dbstat[1];
    $stat_info{$file}{mtime} = $dbstat[9];
}

=head2 retie_db

Tie or Re-tie (if modified) a hash to a certain database file.

Call as:

retie_db(\%db, $filename)

%db is the hash to tie.

$filename is the path to the database

=cut

sub retie_db {
    my($db, $file) = @_;

    _chk_file_stat($file, sub {
        my $file = shift;
        untie %$db if $db;
        my $tieobj;
        $tieobj = tie %$db, DB_File => $file, O_RDONLY, 0, $DB_HASH or
        $tieobj = tie %$db, DB_File => $file, O_RDONLY, 0, $DB_BTREE or
            die "Cannot tie $file: $!\n";
        my $fd = $tieobj->fd;
        my $fh = IO::File->new("<&$fd");
        return $fh;
    });
}

__END__

-- 
Jan-Pieter Cornet <johnpc at xs4all.nl>
!! Disclamer: The addressee of this email is not the intended recipient. !!
!! This is only a test of the echelon and data retention systems. Please !!
!! archive this message indefinitely to allow verification of the logs.  !!



More information about the MIMEDefang mailing list