#-*- perl -*- # # Copyright (C) 2004,2005 Ken'ichi Fukamachi # All rights reserved. This program is free software; you can # redistribute it and/or modify it under the same terms as Perl itself. # # $FML: Merge.pm,v 1.17 2004/12/30 04:35:48 fukachan Exp $ # package FML::Merge; use strict; use vars qw(@ISA @EXPORT @EXPORT_OK $AUTOLOAD); use Carp; =head1 NAME FML::Merge - merge other system configurations to fml8 ones. =head1 SYNOPSIS =head1 DESCRIPTION =head2 new($curproc, $params) constructor. =cut # Descriptions: constructor. # Arguments: OBJ($self) OBJ($curproc) HASH_REF($params) # Side Effects: none # Return Value: OBJ sub new { my ($self, $curproc, $params) = @_; my ($type) = ref($self) || $self; my $me = { _curproc => $curproc, _params => $params, }; # import variables: ml_* ... use FML::Merge::Config; my $m_config = new FML::Merge::Config $params; $me->{ _m_config } = $m_config; # back up to $ml_home_dir/.fml4rc/ directory. # $ml_home_dir/.fml4rc/ # $ml_home_dir/.fml4rc/etc/ use File::Spec; my $ml_home_dir = $m_config->get('ml_home_dir'); if ($ml_home_dir) { my $x_dir = File::Spec->catfile($ml_home_dir, ".fml4rc"); $m_config->set('backup_dir', $x_dir); $curproc->mkdir($x_dir, "mode=private") unless -d $x_dir; $x_dir = File::Spec->catfile($ml_home_dir, ".fml4rc", "etc"); $curproc->mkdir($x_dir, "mode=private") unless -d $x_dir; } else { croak("specify \$ml_home_dir"); } return bless $me, $type; } =head2 set_target_system($system) specify target system. =cut # Descriptions: specify target system. # Arguments: OBJ($self) STR($system) # Side Effects: none # Return Value: none sub set_target_system { my ($self, $system) = @_; # dummy yet. # XXX-TODO: DUMMY. } =head1 BACK UP CONFIGURATION FILES =head2 backup_old_config_files() back up old configuration files. =cut # Descriptions: back up old configuration files. # Arguments: OBJ($self) # Side Effects: move or copy files. # Return Value: none sub backup_old_config_files { my ($self) = @_; my $m_config = $self->{ _m_config }; use FML::Merge::FML4::Config; my $config = new FML::Merge::FML4::Config; my $files = $config->get_old_config_files(); for my $f (@$files) { my $mode = $self->_need_copy() ? "copy" : $config->backup_mode($f); my $src = $m_config->old_file_path($f); my $dst = $m_config->backup_file_path($f); if (-f $src) { if ($mode eq 'move') { printf STDERR "renaming %-30s -> %-30s\n", $src, $dst; rename($src, $dst) || croak("cannot rename $src $dst"); } elsif ($mode eq 'copy') { printf STDERR "copying: %-30s -> %-30s\n", $src, $dst; use IO::Adapter::AtomicFile; IO::Adapter::AtomicFile->copy($src, $dst); unless (-f $dst) { croak("$dst not created"); } } else { print STDERR "error: unknown mode (DO NOTHING).\n"; } } } # continuous use: summary, log, seq ... my $cont_files = $config->get_continuous_use_files(); for my $f (@$cont_files) { my $src = $m_config->backup_file_path($f); my $dst = $m_config->new_file_path($f); printf STDERR "copying: %-30s -> %-30s\n", $src, $dst; use IO::Adapter::AtomicFile; IO::Adapter::AtomicFile->copy($src, $dst); } } # Descriptions: check if we always need copy files to back up dir. # Arguments: OBJ($self) # Side Effects: none # Return Value: NUM(1 or 0) sub _need_copy { my ($self) = @_; my $m_config = $self->{ _m_config }; my $old_home_dir = $m_config->get('src_dir'); my $ml_home_dir = $m_config->get('ml_home_dir'); if ($old_home_dir ne $ml_home_dir) { return 1; } else { return 0; } } =head1 DISABLE INCLUDE FILES To cause temporary failure, disable old include* files by changing it to "exit 75". Code 75 depends on the value of EX_TEMPFAIL of your system. See /usr/include/sysexit.h for more details. For example, the value of NetBSD follows. EX_TEMPFAIL -- temporary failure, indicating something that is not really an error. In sendmail, this means that a mailer (e.g.) could not create a connection, and the request should be reattempted later. =head2 disable_old_include_files() rewrite include* files. =head2 enable_old_include_files() not yet implementd. =cut # Descriptions: rewrite include* files to disable them. # Arguments: OBJ($self) # Side Effects: rewrite include* files. # Return Value: none sub disable_old_include_files { my ($self) = @_; my $m_config = $self->{ _m_config }; use FML::Merge::FML4::Config; my $config = new FML::Merge::FML4::Config; my $files = $config->get_old_include_files(); for my $f (@$files) { my $file = $m_config->old_file_path($f); print STDERR "disable: $file\n"; use IO::Adapter::AtomicFile; IO::Adapter::AtomicFile->copy($file, "$file.bak"); my $wh = new FileHandle "> $file.tmp"; if (defined $wh) { print $wh "exit 75\n"; # EX_TEMPFAIL $wh->close(); unless (rename("$file.tmp", $file)) { croak("fail to rename $file.tmp to $file"); } } else { croak("fail to create $file.tmp"); } } } # Descriptions: rewrite include* files (dummy). # Arguments: OBJ($self) # Side Effects: rewrite include* files. # Return Value: none sub enable_old_include_files { my ($self) = @_; my $m_config = $self->{ _m_config }; use FML::Merge::FML4::Config; my $config = new FML::Merge::FML4::Config; my $files = $config->get_old_include_files(); for my $f (@$files) { my $file = $m_config->old_file_path($f); print STDERR "enable $file\n"; print STDERR " mv $file.bak $file\n"; } } =head1 CONVERT USER LIST FILES. =head2 convert_list_files() convert fml4 list files to fml8 style ones. =cut # Descriptions: convert fml4 list files to fml8 style ones. # Arguments: OBJ($self) # Side Effects: old fml4 files moved to .fml4rc/, # fml8 files created if needed. # Return Value: none sub convert_list_files { my ($self) = @_; my $curproc = $self->{ _curproc }; my $m_config = $self->{ _m_config }; use FML::Merge::FML4::List; my $list = new FML::Merge::FML4::List $curproc, $m_config; $list->convert(); } =head1 MERGE =head2 merge_into_config_cf() merge fml4 config.ph into fm8 config.cf file. =cut # Descriptions: merge fml4 config.ph into fm8 config.cf file. # Arguments: OBJ($self) # Side Effects: none # Return Value: none sub merge_into_config_cf { my ($self) = @_; my $m_config = $self->{ _m_config }; # check config.ph path files. my $old_config_ph = $m_config->old_file_path("config.ph"); my $default_config_ph = $self->speculate_default_config_ph_path(); use FML::Merge::FML4::config_ph; my $config_ph = new FML::Merge::FML4::config_ph; $config_ph->set_default_config_ph($default_config_ph); my $diff = $config_ph->diff($old_config_ph); $self->_inject_into_config_cf($diff); } # Descriptions: speculate the path of default_config.ph file. # Arguments: OBJ($self) # Side Effects: none # Return Value: STR sub speculate_default_config_ph_path { my ($self) = @_; my $curproc = $self->{ _curproc }; my $m_config = $self->{ _m_config }; my $old_include_path = $m_config->old_file_path("include"); my $bak_include_path = $m_config->backup_file_path("include"); my $path = ''; use FileHandle; use File::Basename; my $rh = undef; if (-f $bak_include_path) { $rh = new FileHandle $bak_include_path; } elsif (-f $old_include_path) { $rh = new FileHandle $old_include_path; } if (defined $rh) { my $buf; BUF: while ($buf = <$rh>) { if ($buf =~ /^\s*\"\|\s*(\S+fml\.pl)/) { $path = $1; $path =~ s/\|//og; $path =~ s/\"//og; $path = dirname($path); last BUF; } } $rh->close(); } use File::Spec; my $file = File::Spec->catfile($path, "default_config.ph"); if (-f $file) { print STDERR " using $file as default_config.ph\n"; return $file; } else { my $config = $curproc->config(); my $file = $config->{ compat_old_fml_default_config_ph_file }; if (-f $file) { print STDERR " using $file as default_config.ph\n"; return $file; } else { croak("default_config.ph path undefined."); } } } # Descriptions: inject config.ph summary to config.cf file. # Arguments: OBJ($self) HASH_REF($diff) # Side Effects: rewrite config.cf. # Return Value: none sub _inject_into_config_cf { my ($self, $diff) = @_; my $m_config = $self->{ _m_config }; my $config_cf = $m_config->new_file_path("config.cf"); my $tmp = "$config_cf.new.$$"; print STDERR "merging: $config_cf\n"; my $rh = new FileHandle $config_cf; my $wh = new FileHandle "> $tmp"; if (defined $rh && defined $wh) { my $buf; LINE: while ($buf = <$rh>) { if ($buf =~ /^=cut/o) { $self->_inject_diff_into_config_cf($wh, $diff); } print $wh $buf; } $wh->close(); $rh->close(); unless (rename($tmp, $config_cf)) { croak("cannot rename $tmp to $config_cf"); } } } # Descriptions: inject config.ph summary to config.cf file. # Arguments: OBJ($self) HANDLE($wh) HASH_REF($diff) # Side Effects: rewrite config.cf. # Return Value: none sub _inject_diff_into_config_cf { my ($self, $wh, $diff) = @_; my ($k, $v, $x, $y); print $wh "\n"; print $wh "# \n"; print $wh "\n"; use FML::Merge::FML4::config_ph; my $config_ph = new FML::Merge::FML4::config_ph; for my $k (sort _sort_order keys %$diff) { $v = $diff->{ $k }; $y = $v; $y =~ s/\n/\n# /gm; print $wh "# \$$k => $y\n"; if ($x = $config_ph->translate($diff, $k, $v)) { print $wh $x ,"\n\n"; } else { print $wh "\n"; } } print $wh "\n"; print $wh "# \n"; print $wh "\n"; } # Descriptions: tune sort order: postpone PROC__* and *_HOOK variables. # Arguments: OBJ($self) # Side Effects: none # Return Value: NUM sub _sort_order { my $x = $a; my $y = $b; $x = "zz_$x" if $x =~ /^PROC__/o; $y = "zz_$y" if $y =~ /^PROC__/o; $x = "zzz_$x" if $x =~ /HOOK/o; $y = "zzz_$y" if $y =~ /HOOK/o; $x cmp $y; } =head1 CODING STYLE See C on fml coding style guide. =head1 AUTHOR Ken'ichi Fukamachi =head1 COPYRIGHT Copyright (C) 2004,2005 Ken'ichi Fukamachi All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 HISTORY FML::Merge appeared in fml8 mailing list driver package. See C for more details. =cut 1;