#!/usr/local/bin/perl ##--------------------------------------------------------------------------## ## File: ## $Id: install.pl,v 1.8 2003/08/09 17:48:34 ehood Exp $ ## Description: ## See POD below or run program with -man option. ##--------------------------------------------------------------------------## ## Copyright (C) 2002 Earl Hood ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ## 02111-1307, USA ##--------------------------------------------------------------------------## package MHArc::install; ##--------------------------------------------------------------------------## BEGIN { die qq/CGI use FORBIDDEN!\n/ if (defined($ENV{'GATEWAY_INTERFACE'})); } my $Dir; BEGIN { $Dir = `dirname $0`; chomp $Dir; } use lib "$Dir/lib"; # Add relative lib to search path ##--------------------------------------------------------------------------## use Config; use Cwd; use Getopt::Long; use MHArc::Config; use MHArc::Util qw( usage run_prg cmd ch_dir ); my $Batch = 0; my $Debug = 0; my $Verbose = 1; my $Perl = undef; my $InstallDir = undef; my $Url = undef; my $Make = undef; my $Mknmz = undef; my $Nmzcgi = undef; my $Mhonarc = undef; my $Tar = undef; my $Cp = undef; my $Mkdir = undef; my $Pwd = undef; my $Procmail = undef; my $Formail = undef; my $Lockfile = undef; my @PerlScripts = (qw( bin/apply-config bin/compress-files bin/compress-mboxes bin/config-check bin/extract-mesg-date bin/filter-spool bin/gc-search-indexes bin/mbox-month-pack bin/mh-month-pack bin/mhonarc-check bin/mk-procmailrc bin/read-mail bin/web-archive )); # We prepend pathname components during installation my @CgiScripts = qw( extract-mesg.cgi.in.dist mesg.cgi.in.dist mnav.cgi.in.dist ); ##--------------------------------------------------------------------------## MAIN: { # Grap command-line options my $clstatus = GetOptions( 'batch' => \$Batch, 'path=s' => \$InstallDir, 'perl=s' => \$Perl, 'url=s' => \$Url, 'debug!' => \$Debug, 'verbose!' => \$Verbose, 'help' => \$help, 'man' => \$man ); usage(0) unless $clstatus; usage(1) if $help; usage(2) if $man; $Verbose = 1 if $Debug; $MHArc::Util::ECHO_CMDS = $Debug; ch_dir($Dir) || die qq/ERROR: Unable to chdir to "$Dir": $!\n/; $Make = find_make() || 'make'; $Tar = find_program('tar') || 'tar'; $Cp = find_program('cp') || 'cp'; $Mkdir = find_program('mkdir') || 'mkdir'; $Pwd = find_program('pwd') || 'pwd'; $Perl = $Config{'perlpath'} unless defined($Perl); $Mhonarc = find_mhonarc(); $Mknmz = find_mknmz(); $Nmzcgi = find_namazu_cgi(); $Procmail = find_program('procmail'); $Formail = find_program('formail'); $Lockfile = find_program('lockfile'); my $run_config = 0; my $run_procmailrc = 0; my $tar_v = 'v' if $Debug; # Create installation directory if (defined($InstallDir)) { if (! -e $InstallDir) { run_prg($Mkdir, '-p', $InstallDir); } elsif (! -w $InstallDir) { die qq/ERROR: You do not have write permission to "$InstallDir"!\n/; } } else { die qq/ERROR: Must specify installation path via -path when -batch /, qq/is used!\n/ if $Batch; while (1) { $InstallDir = interpolate_path( prompt_user('Pathname to install mharc:')); next unless $InstallDir =~ /\S/; if (-e $InstallDir) { if (! -d $InstallDir) { warn qq/"$InstallDir" is not a directory!\n/; next; } if (! -w $InstallDir) { warn qq/"$InstallDir" is not a writable!\n/; next; } last; } else { next unless prompt_user_yn( qq/"$InstallDir" does not exist, create?/, 1); last if (cmd($Mkdir, '-p', $InstallDir) == 0); } } } my $cwd_path = Cwd::abs_path('.'); $InstallDir = Cwd::abs_path($InstallDir); print qq/Current working directory: $cwd_path\n/ if $Debug; # Check if doing an upgrade my $cur_config = undef; my $cur_mbox_dir = undef; my $cur_html_dir = undef; my $cur_cgi_dir = undef; if (-e "$InstallDir/lib/config.sh") { $cur_config = MHArc::Config->load("$InstallDir/lib/config.sh"); $cur_mbox_dir = Cwd::abs_path($cur_config->{'MBOX_DIR'}); $cur_html_dir = Cwd::abs_path($cur_config->{'HTML_DIR'}); $cur_cgi_dir = Cwd::abs_path($cur_config->{'CGI_DIR'}) if defined($cur_config->{'CGI_DIR'}); } if (!$cur_mbox_dir) { $cur_mbox_dir = "$InstallDir/mbox"; } if (!$cur_html_dir) { $cur_html_dir = "$InstallDir/html"; } if (!$cur_cgi_dir) { $cur_cgi_dir = "$InstallDir/cgi-bin"; } # make sure that html and cgi-bin directories exist run_prg($Mkdir, '-p', $cur_mbox_dir) unless -e $cur_mbox_dir; run_prg($Mkdir, '-p', $cur_html_dir) unless -e $cur_html_dir; run_prg($Mkdir, '-p', $cur_cgi_dir) unless -e $cur_cgi_dir; # Grab list of files to copy local(*DIR); opendir(DIR, '.') || die qq/ERROR: Unable to open "$cwd_path": $!\n/; my @copy_files = grep { !/^\./ && ($_ ne 'install.pl') && # ($_ ne 'INSTALL') && # ($_ ne 'README') && # ($_ ne 'NEWS') && # ($_ ne 'TODO') && ($_ ne 'dist') && ($_ ne 'mbox') && ($_ ne 'html') && ($_ ne 'cgi-bin') } readdir(DIR); closedir(DIR); # Copy files (nothing for mbox, so we do not mess with it, yet) if ($InstallDir ne $cwd_path) { print qq/Copying files into "$InstallDir"...\n/ if $Verbose; run_prg("$Tar cf - @copy_files | (cd \"$InstallDir\" && tar x${tar_v}f -)"); } if ($cur_html_dir ne "$cwd_path/html") { print qq/Copying files into "$cur_html_dir"...\n/ if $Verbose; ch_dir('html') || die qq/ERROR: Unable to chdir to "html": $!\n/; run_prg("$Tar cf - . | (cd \"$cur_html_dir\" && tar x${tar_v}f -)"); ch_dir($cwd_path) || die qq/ERROR: Unable to chdir to "$cwd_path": $!\n/; } if ($cur_cgi_dir ne "$cwd_path/cgi-bin") { print qq/Copying files into "$cur_cgi_dir"...\n/ if $Verbose; ch_dir('cgi-bin') || die qq/ERROR: Unable to chdir to "cgi-bin": $!\n/; run_prg("$Tar cf - . | (cd \"$cur_cgi_dir\" && tar x${tar_v}f -)"); ch_dir($cwd_path) || die qq/ERROR: Unable to chdir to "$cwd_path": $!\n/; } # We now work in the installation directory ch_dir($InstallDir) || die qq/ERROR: Unable to chdir to "$InstallDir": $!\n/; my @cgi_scripts = map { "$cur_cgi_dir/$_" } @CgiScripts; # Edit Perl scripts to reference where perl is located edit_perl_scripts($Perl, @PerlScripts, @cgi_scripts); # Check CGI programs if (check_cgi_scripts(@cgi_scripts)) { $run_config = 1; } # config.sh variables we will define if new installation my @vars = ( 'SW_ROOT' => $InstallDir ); # check for mhonarc library if (defined($cur_config) && $cur_config->{'MHONARC_LIB'}) { eval { require join('/', $cur_config->{'MHONARC_LIB'}, 'mhamain.pl'); }; if ($@) { warn qq/WARNING: Unable to load MHonArc: $@\n/; } } elsif (defined($Mhonarc)) { my $mha_lib = extract_mhonarc_lib($Mhonarc); if (defined($mha_lib)) { eval { require "$mha_lib/mhamain.pl"; }; if ($@) { warn qq/WARNING: Unable to load MHonArc: $@\n/; } else { push(@vars, 'MHONARC_LIB' => $mha_lib); } } } else { print < $Mknmz); } # check for procmail if (!defined($Procmail)) { print < $Procmail); } # check for formail if (!defined($Formail)) { print < $Formail); } # check for lockfile if (!defined($Lockfile)) { print < $Lockfile); } # Check that namazu.cgi exists in cgi-bin if (! -e "$cur_cgi_dir/namazu.cgi") { if (!defined($Nmzcgi)) { print < $Url); } } edit_config_sh('lib/config.sh.dist', @vars); if (!defined($cur_config)) { run_prg($Cp, 'lib/config.sh.dist', 'lib/config.sh'); $run_config = 1; } else { if (verify_config_sh('lib/config.sh', $cur_config, MHArc::Config->load('lib/config.sh.dist'))) { $run_config = 1; } } if (manual_file_edit('lib/config.sh', 1)) { $run_config = 1; } # If a new install, create lists.def if (! -e 'lib/lists.def') { run_prg($Cp, 'lib/lists.def.dist', 'lib/lists.def'); if (manual_file_edit('lib/lists.def', 1)) { $run_procmailrc = 1; } } # Check that mhonarc can be loaded cmd('bin/mhonarc-check'); # Apply config, if needed apply_config() if $run_config; # Create procmailrc run_prg('bin/mk-procmailrc') if $run_procmailrc; print <'.$tmp_file)) { die qq/ERROR: Unable to create "$tmp_file": $!\n/; } if (!open(SIN, $script)) { close(SOUT); unlink($tmp_file); die qq/ERROR: Unable to open "$script": $!\n/; } print SOUT '#!', $perl, "\n"; my $first_line = ; if (defined($first_line)) { print SOUT $first_line unless $first_line =~ /^#!/; print SOUT ; } close SOUT; close SIN; if (!rename($tmp_file, $script)) { die qq/ERROR: Unable to rename "$tmp_file" to "$script": $!\n/; } if (!chmod(0755, $script)) { die qq/ERROR: Problem to changing permissions for $script: $!\n/; } } } sub check_cgi_scripts { my $script; my $did_change = 0; foreach $script (@_) { my $in = $script; $in =~ s/\.dist$//; my $cgi = $in; $cgi =~ s/\.in$//; next if ($in eq $script); if (! -e $in) { $did_change = 1; next; } if (scripts_differ($script, $in)) { if ($Batch || prompt_user_yn(join("\n", "\nNew \"$cgi\" differs from existing, replace?", "(Answer 'n' if you have made custom changes to", " \"$in\")"), 1)) { if (!unlink($in)) { warn qq/WARNING: Unable to remove "$in": $!\n/; } $did_change = 1; } } } $did_change; } sub scripts_differ { my $s1 = shift; my $s2 = shift; my $answer = 0; local(*S1, *S2); if (!open(S1, $s1)) { die qq/ERROR: Unable to open "$s1": $!\n/; } if (!open(S2, $s2)) { close S1; die qq/ERROR: Unable to open "$s2": $!\n/; } # skip #! line scalar(); scalar(); my($l1, $l2); while (defined($l1 = )) { if (!defined($l2 = scalar())) { $answer = 1; last; } if ($l1 ne $l2) { $answer = 1; last; } } $answer = 1 if !eof(S2); close S1; close S2; $answer; } sub find_program { my $program = shift; my @variants = @_; if (!@variants) { push(@variants, $program); } print qq/Looking for '$program' program.../ if $Verbose; my @path = split(/:/, $ENV{'PATH'}); # append paths we should always check push(@path, '/usr/local/bin', '/usr/bin', '/bin', '/opt/sfw/bin', # for Solaris '/usr/ccs/bin'); # for Solaris my($p, $v, $prg); PATH: foreach $p (@path) { foreach $v (@variants) { $prg = "$p/$v"; last PATH if (-x $prg); } $prg = undef; } if (!defined($prg)) { print qq/ NOT FOUND!\n/ if $Verbose; return undef; } print qq/ '$prg'\n/ if $Verbose; $prg; } sub find_make { find_program('make', 'gmake', 'make'); } sub find_mhonarc { find_program('mhonarc'); } sub find_mknmz { find_program('mknmz'); } sub find_namazu_cgi { print qq/Looking for 'namazu.cgi'.../ if $Verbose; my($p, $cgi); foreach $p (join('/', $ENV{'HOME'}, 'libexec'), qw( /usr/local/libexec /usr/local/namazu/libexec /usr/libexec /usr/namazu/libexec /var/www/cgi-bin /usr/lib/cgi-bin /usr/local/lib/cgi-bin /home/httpd/cgi-bin )) { $cgi = "$p/namazu.cgi"; last if (-x $cgi); $cgi = undef; } if (!defined($cgi)) { print qq/ NOT FOUND!\n/ if $Verbose; return undef; } print qq/ '$cgi'\n/ if $Verbose; $cgi; } sub extract_mhonarc_lib { my $mhonarc = shift; my $libpath = undef; print qq/Determine MHonArc library path from '$mhonarc'.../ if $Verbose; local(*MHA); if (!open(MHA, $mhonarc)) { die qq/\nERROR: Unable to open "$mhonarc": $!\n/; } local $_; while () { next unless /^use lib qw\(([^)]+)\)/; $libpath = $1; last; } close MHA; if (!defined($libpath)) { print qq/ NOT FOUND\n/ if $Verbose; } else { print qq/ '$libpath'\n/ if $Verbose; } $libpath; } sub interpolate_path { my($path) = shift; $path =~ s/^~(\/|$)/$ENV{'HOME'}$1/o; $path =~ s/^(~\w+)(\/|$)/get_user_home_dir($1).$2/oe; $path =~ s/\$(\w+)/defined($ENV{$1})?$ENV{$1}:"\$$1"/ge; $path =~ s/\$\{(\w+)\}/defined($ENV{$1})?$ENV{$1}:"\${$1}"/ge; $path; } sub get_user_home_dir { my $orguser = shift; my $user = $orguser; $user =~ s/~//g; my @pwent = getpwnam($user); return scalar(@pwent) ? $pwent[7] : $orguser; } sub prompt_user { my $prompt = shift; my $default = shift; my($answer); print STDOUT "\n", $prompt; print STDOUT qq{ ("$default")} if defined($default); print STDOUT " "; $answer = ; chomp $answer; $answer = $default if $answer !~ /\S/; $answer; } sub edit_config_sh { my $file = shift; my %vars = @_; local(*CFG_OUT, *CFG_IN); my $file_tmp = $file . '.tmp.' . $$; if (!open(CFG_IN, $file)) { die qq/ERROR: Unable to open "$file": $!\n/; } if (!open(CFG_OUT, '>'.$file_tmp)) { close(CFG_IN); die qq/ERROR: Unable to create "$file_tmp": $!\n/; } my %found = ( ); local $_; while () { if (/^([^=\s]+)\s*=/) { if (defined($vars{$1})) { my $var = $1; my $val = $vars{$var}; if ($val =~ /\s/) { $val = '"' . $val . '"'; } #print CFG_OUT '#', $_; print CFG_OUT $var, '=', $val, "\n"; $found{$var} = 1; next; } } print CFG_OUT $_; } foreach (sort keys %vars) { next if $found{$_}; print CFG_OUT "\n", '# Added by install.pl on ', scalar(localtime(time)), "\n"; print CFG_OUT $_, '='; if ($vars{$_} =~ /\s/) { print CFG_OUT '"', $vars{$_}, '"'; } else { print CFG_OUT $vars{$_}; } print CFG_OUT "\n"; } close CFG_OUT; close CFG_IN; if (!rename($file_tmp, $file)) { die qq/ERROR: Unable to rename "$file_tmp" to "$file": $!\n/; } } sub verify_config_sh { my $filename = shift; my $config = shift; my $dist_config = shift; my @check_vars = qw( CGI_DIR CGI_URL FORMAIL LOCKFILE MKNMZ PROCMAIL ); my(@add_vars) = ( ); my($var); foreach $var (@check_vars) { if (!$config->{$var}) { push(@add_vars, $var => $dist_config->{$var}); print qq/NOTE: Adding $var="/, $dist_config->{$var}, qq/" to $filename\n/; } } if (@add_vars) { edit_config_sh($filename, @add_vars); return 1; } 0; } sub manual_file_edit { return 0 if $Batch; my $file = shift; my $default = shift; my $editor = $ENV{'EDITOR'} || 'vi'; if (prompt_user_yn(qq/Would you like to edit "$file" with "$editor"?/, $default)) { if (cmd($editor, $file) != 0) { warn qq/WARNING: "$editor" exited with non-zero status: $?\n/; } return 1; } 0; } sub prompt_user_yn { my $prompt = shift; my $default = shift; my($answer); print STDOUT $prompt, " "; print STDOUT $default ? "['y']" : "['n']"; print STDOUT " "; $answer = ; chomp $answer; if ($answer !~ /\S/) { $answer = $default; } elsif ($answer =~ /q/i or $answer =~ /quit/i) { print STDOUT "Installation aborted!\n"; exit(0); } elsif ($answer =~ /y/i or $answer =~ /yes/i) { $answer = 1; } else { $answer = 0; } $answer; } ##--------------------------------------------------------------------------## __END__ =head1 NAME install.pl - Installation script for mharc =head1 SYNOPSIS install.pl install.pl [options] =head1 DESCRIPTION This program installs mharc into a specified location. This installation documentation that comes with mharc should be read to complete the installation process since you may need to perform other actions that are not automatically performed by this program. =head1 OPTIONS =over =item C<-batch> Do not prompt for anything. All prompts will default to the default choice (which may not be what you want). If this option is specified, then C<-path> must also be specified. =item C<-debug> Generate more output than C<-verbose>. This option can generate alot of output. =item C<-help> Print out help message. =item C<-man> Print out the manpage. =item C<-path> I Pathname to install mharc. If not specified, the path will be prompted for. This option must be specified if C<-batch> is used. =item C<-perl> I Pathname to perl. By default, the pathname for the perl executing this program will be used. =item C<-url> I Root URL to archives. This is only used for a new install. If not specified, the value will be prompted for. If you do not know the answer, you can leave it blank and define the C variable in C later and run 'C' to have the setting applied. =item C<-verbose> Print status of what is going on. This is the default. To keep message to a minimum, you can negate this option with C<-noverbose>. =back =head1 NOTES =over =item * This installation documentation should be read to complete the installation process since you may need to perform other actions that are not automatically performed by this program. =back =head1 VERSION $Id: install.pl,v 1.8 2003/08/09 17:48:34 ehood Exp $ =head1 AUTHOR Earl Hood, earl@earlhood.com This program is part of the mharc archiving system and comes with ABSOLUTELY NO WARRANTY and may be copied only under the terms of the GNU General Public License, which may be found in the mharc distribution. =cut