bb86691d3b34d2096134d31a0a77f1b212cd645b
[BackupPC.git] / configure.pl
1 #!/bin/perl
2 #============================================================= -*-perl-*-
3 #
4 # configure.pl: Configuration and installation program for BackupPC
5 #
6 # DESCRIPTION
7 #
8 #   This script should be run as root:
9 #
10 #        perl configure.pl
11 #
12 #   To read about the command-line options for this configure script:
13 #
14 #        perldoc configure.pl
15 #
16 #   The installation steps are described as the script runs.
17 #
18 # AUTHOR
19 #   Craig Barratt <cbarratt@users.sourceforge.net>
20 #
21 # COPYRIGHT
22 #   Copyright (C) 2001-2004  Craig Barratt
23 #
24 #   This program is free software; you can redistribute it and/or modify
25 #   it under the terms of the GNU General Public License as published by
26 #   the Free Software Foundation; either version 2 of the License, or
27 #   (at your option) any later version.
28 #
29 #   This program is distributed in the hope that it will be useful,
30 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
31 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32 #   GNU General Public License for more details.
33 #
34 #   You should have received a copy of the GNU General Public License
35 #   along with this program; if not, write to the Free Software
36 #   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
37 #
38 #========================================================================
39 #
40 # Version 2.1.0beta1, released 9 Apr 2004.
41 #
42 # See http://backuppc.sourceforge.net.
43 #
44 #========================================================================
45
46 use strict;
47 no  utf8;
48 use vars qw(%Conf %OrigConf);
49 use lib "./lib";
50
51 my @Packages = qw(File::Path File::Spec File::Copy DirHandle Digest::MD5
52                   Data::Dumper Getopt::Std Getopt::Long Pod::Usage
53                   BackupPC::Lib BackupPC::FileZIO);
54
55 foreach my $pkg ( @Packages ) {
56     eval "use $pkg";
57     next if ( !$@ );
58     if ( $pkg =~ /BackupPC/ ) {
59         die <<EOF;
60
61 BackupPC cannot find the package $pkg, which is included in the
62 BackupPC distribution.  This probably means you did not cd to the
63 unpacked BackupPC distribution before running configure.pl, eg:
64
65     cd BackupPC-__VERSION__
66     ./configure.pl
67
68 Please try again.
69
70 EOF
71     }
72     die <<EOF;
73
74 BackupPC needs the package $pkg.  Please install $pkg
75 before installing BackupPC.
76
77 EOF
78 }
79
80 my %opts;
81 if ( !GetOptions(
82             \%opts,
83             "batch",
84             "bin-path=s%",
85             "config-path=s",
86             "cgi-dir=s",
87             "data-dir=s",
88             "dest-dir=s",
89             "help|?",
90             "hostname=s",
91             "html-dir=s",
92             "html-dir-url=s",
93             "install-dir=s",
94             "man",
95             "uid-ignore",
96         ) || @ARGV ) {
97     pod2usage(2);
98 }
99 pod2usage(1) if ( $opts{help} );
100 pod2usage(-exitstatus => 0, -verbose => 2) if $opts{man};
101
102 my $DestDir = $opts{"dest-dir"};
103
104 if ( $< != 0 ) {
105     print <<EOF;
106
107 This configure script should be run as root, rather than uid $<.
108 Provided uid $< has sufficient permissions to create the data and
109 install directories, then it should be ok to proceed.  Otherwise,
110 please quit and restart as root.
111
112 EOF
113     exit(1) if ( prompt("--> Do you want to continue?",
114                        "y") !~ /y/i );
115     exit(1) if ( $opts{batch} && !$opts{"uid-ignore"} );
116 }
117
118 print <<EOF;
119
120 Is this a new installation or upgrade for BackupPC?  If this is
121 an upgrade please tell me the full path of the existing BackupPC
122 configuration file (eg: /xxxx/conf/config.pl).  Otherwise, just
123 hit return.
124
125 EOF
126
127 #
128 # Check if this is an upgrade, in which case read the existing
129 # config file to get all the defaults.
130 #
131 my $ConfigPath = "";
132 while ( 1 ) {
133     $ConfigPath = prompt("--> Full path to existing conf/config.pl",
134                          $ConfigPath,
135                          "config-path");
136     last if ( $ConfigPath eq ""
137             || ($ConfigPath =~ /^\// && -r $ConfigPath && -w $ConfigPath) );
138     my $problem = "is not an absolute path";
139     $problem = "is not writable" if ( !-w $ConfigPath );
140     $problem = "is not readable" if ( !-r $ConfigPath );
141     $problem = "doesn't exist"   if ( !-f $ConfigPath );
142     print("The file '$ConfigPath' $problem.\n");
143     if ( $opts{batch} ) {
144         print("Need to specify a valid --config-path for upgrade\n");
145         exit(1);
146     }
147 }
148 my $bpc;
149 if ( $ConfigPath ne "" && -r $ConfigPath ) {
150     (my $topDir = $ConfigPath) =~ s{/[^/]+/[^/]+$}{};
151     die("BackupPC::Lib->new failed\n")
152             if ( !($bpc = BackupPC::Lib->new($topDir, ".", 1)) );
153     %Conf = $bpc->Conf();
154     %OrigConf = %Conf;
155     $Conf{TopDir} = $topDir;
156     my $err = $bpc->ServerConnect($Conf{ServerHost}, $Conf{ServerPort}, 1);
157     if ( $err eq "" ) {
158         print <<EOF;
159
160 BackupPC is running on $Conf{ServerHost}.  You need to stop BackupPC
161 before you can upgrade the code.  Depending upon your installation,
162 you could run "/etc/init.d/backuppc stop".
163
164 EOF
165         exit(1);
166     }
167 }
168
169 #
170 # These are the programs whose paths we need to find
171 #
172 my %Programs = (
173     perl           => "PerlPath",
174     'gtar/tar'     => "TarClientPath",
175     smbclient      => "SmbClientPath",
176     nmblookup      => "NmbLookupPath",
177     rsync          => "RsyncClientPath",
178     ping           => "PingPath",
179     df             => "DfPath",
180     'ssh/ssh2'     => "SshPath",
181     sendmail       => "SendmailPath",
182     hostname       => "HostnamePath",
183     split          => "SplitPath",
184     par2           => "ParPath",
185     cat            => "CatPath",
186     gzip           => "GzipPath",
187     bzip2          => "Bzip2Path",
188 );
189
190 foreach my $prog ( sort(keys(%Programs)) ) {
191     my $path;
192     foreach my $subProg ( split(/\//, $prog) ) {
193         $path = FindProgram("$ENV{PATH}:/bin:/usr/bin:/sbin:/usr/sbin",
194                             $subProg) if ( !length($path) );
195     }
196     $Conf{$Programs{$prog}} = $path if ( !length($Conf{$Programs{$prog}}) );
197 }
198
199 while ( 1 ) {
200     print <<EOF;
201
202 I found the following locations for these programs:
203
204 EOF
205     foreach my $prog ( sort(keys(%Programs)) ) {
206         printf("    %-12s => %s\n", $prog, $Conf{$Programs{$prog}});
207     }
208     print "\n";
209     last if (prompt('--> Are these paths correct?', 'y') =~ /^y/i);
210     foreach my $prog ( sort(keys(%Programs)) ) {
211         $Conf{$Programs{$prog}} = prompt("--> $prog path",
212                                          $Conf{$Programs{$prog}});
213     }
214 }
215
216 my $Perl56 = system($Conf{PerlPath}
217                         . q{ -e 'exit($^V && $^V ge v5.6.0 ? 1 : 0);'});
218
219 if ( !$Perl56 ) {
220     print <<EOF;
221
222 BackupPC needs perl version 5.6.0 or later.  $Conf{PerlPath} appears
223 to be an older version.  Please upgrade to a newer version of perl
224 and re-run this configure script.
225
226 EOF
227     exit(1);
228 }
229
230 print <<EOF;
231
232 Please tell me the hostname of the machine that BackupPC will run on.
233
234 EOF
235 chomp($Conf{ServerHost} = `$Conf{HostnamePath}`)
236         if ( defined($Conf{HostnamePath}) && !defined($Conf{ServerHost}) );
237 $Conf{ServerHost} = prompt("--> BackupPC will run on host",
238                            $Conf{ServerHost},
239                            "hostname");
240
241 print <<EOF;
242
243 BackupPC should run as a dedicated user with limited privileges.  You
244 need to create a user.  This user will need read/write permission on
245 the main data directory and read/execute permission on the install
246 directory (these directories will be setup shortly).
247
248 The primary group for this user should also be chosen carefully.
249 By default the install directories will have group write permission.
250 The data directories and files will have group read permission but
251 no other permission.
252
253 EOF
254 my($name, $passwd, $Uid, $Gid);
255 while ( 1 ) {
256     $Conf{BackupPCUser} = prompt("--> BackupPC should run as user",
257                                  $Conf{BackupPCUser} || "backuppc",
258                                  "username");
259     ($name, $passwd, $Uid, $Gid) = getpwnam($Conf{BackupPCUser});
260     last if ( $name ne "" );
261     print <<EOF;
262
263 getpwnam() says that user $Conf{BackupPCUser} doesn't exist.  Please check the
264 name and verify that this user is in the passwd file.
265
266 EOF
267     exit(1) if ( $opts{batch} );
268 }
269
270 print <<EOF;
271
272 Please specify an install directory for BackupPC.  This is where the
273 BackupPC scripts, library and documentation will be installed.
274
275 EOF
276
277 while ( 1 ) {
278     $Conf{InstallDir} = prompt("--> Install directory (full path)",
279                                $Conf{InstallDir},
280                                "install-dir");
281     last if ( $Conf{InstallDir} =~ /^\// );
282     if ( $opts{batch} ) {
283         print("Need to specify --install-dir for new installation\n");
284         exit(1);
285     }
286 }
287
288 print <<EOF;
289
290 Please specify a data directory for BackupPC.  This is where the
291 configuration files, LOG files and all the PC backups are stored.
292 This file system needs to be big enough to accommodate all the
293 PCs you expect to backup (eg: at least 1-2GB per machine).
294
295 EOF
296
297 while ( 1 ) {
298     $Conf{TopDir} = prompt("--> Data directory (full path)",
299                            $Conf{TopDir},
300                            "data-dir");
301     last if ( $Conf{TopDir} =~ /^\// );
302     if ( $opts{batch} ) {
303         print("Need to specify --data-dir for new installation\n");
304         exit(1);
305     }
306 }
307
308 if ( !defined($Conf{CompressLevel}) ) {
309     $Conf{CompressLevel} = BackupPC::FileZIO->compOk ? 3 : 0;
310     if ( $ConfigPath eq "" && $Conf{CompressLevel} ) {
311         print <<EOF;
312
313 BackupPC can compress pool files, providing around a 40% reduction in pool
314 size (your mileage may vary). Specify the compression level (0 turns
315 off compression, and 1 to 9 represent good/fastest to best/slowest).
316 The recommended values are 0 (off) or 3 (reasonable compression and speed).
317 Increasing the compression level to 5 will use around 20% more cpu time
318 and give perhaps 2-3% more compression.
319
320 EOF
321     } elsif ( $ConfigPath eq "" ) {
322         print <<EOF;
323
324 BackupPC can compress pool files, but it needs the Compress::Zlib
325 package installed (see www.cpan.org). Compression will provide around a
326 40% reduction in pool size, at the expense of cpu time.  You can leave
327 compression off and run BackupPC without compression, in which case you
328 should leave the compression level at 0 (which means off).  You could
329 install Compress::Zlib and turn compression on later, but read the
330 documentation first about how to do this.  Or the better choice is
331 to quit, install Compress::Zlib, and re-run configure.pl.
332
333 EOF
334     } elsif ( $Conf{CompressLevel} ) {
335         $Conf{CompressLevel} = 0;
336         print <<EOF;
337
338 BackupPC now supports pool file compression.  Since you are upgrading
339 BackupPC you probably have existing uncompressed backups.  You have
340 several choices if you want to turn on compression.  You can run
341 the script BackupPC_compressPool to convert everything to compressed
342 form.  Or you can simply turn on compression, so that new backups
343 will be compressed.  This will increase the pool storage requirement,
344 since both uncompressed and compressed copies of files will be stored.
345 But eventually the old uncompressed backups will expire, recovering
346 the pool storage.  Please see the documentation for more details.
347
348 If you are not sure what to do, leave the Compression Level at 0,
349 which disables compression.  You can always read the documentation
350 and turn it on later.
351
352 EOF
353     } else {
354         $Conf{CompressLevel} = 0;
355         print <<EOF;
356
357 BackupPC now supports pool file compression, but it needs the
358 Compress::Zlib module (see www.cpan.org).  For now, leave
359 the compression level set at 0 to disable compression.  If you
360 want you can install Compress::Zlib and turn compression on.
361 Please see the documentation for more details about converting
362 old backups to compressed form.
363
364 EOF
365     }
366     while ( 1 ) {
367         $Conf{CompressLevel}
368                     = prompt("--> Compression level", $Conf{CompressLevel});
369         last if ( $Conf{CompressLevel} =~ /^\d+$/ );
370     }
371 }
372
373 print <<EOF;
374
375 BackupPC has a powerful CGI perl interface that runs under Apache.
376 A single executable needs to be installed in a cgi-bin directory.
377 This executable needs to run as set-uid $Conf{BackupPCUser}, or
378 it can be run under mod_perl with Apache running as user $Conf{BackupPCUser}.
379
380 Leave this path empty if you don't want to install the CGI interface.
381
382 EOF
383
384 while ( 1 ) {
385     $Conf{CgiDir} = prompt("--> CGI bin directory (full path)",
386                            $Conf{CgiDir},
387                            "cgi-dir");
388     last if ( $Conf{CgiDir} =~ /^\// || $Conf{CgiDir} eq "" );
389     if ( $opts{batch} ) {
390         print("Need to specify --cgi-dir for new installation\n");
391         exit(1);
392     }
393 }
394
395 if ( $Conf{CgiDir} ne "" ) {
396
397     print <<EOF;
398
399 BackupPC's CGI script needs to display various GIF images that
400 should be stored where Apache can serve them.  They should be
401 placed somewher under Apache's DocumentRoot.  BackupPC also
402 needs to know the URL to access these images.  Example:
403
404     Apache image directory:  /usr/local/apache/htdocs/BackupPC
405     URL for image directory: /BackupPC
406
407 The URL for the image directory should start with a slash.
408
409 EOF
410     while ( 1 ) {
411         $Conf{CgiImageDir} = prompt("--> Apache image directory (full path)",
412                                     $Conf{CgiImageDir},
413                                     "html-dir");
414         last if ( $Conf{CgiImageDir} =~ /^\// );
415         if ( $opts{batch} ) {
416             print("Need to specify --html-dir for new installation\n");
417             exit(1);
418         }
419     }
420     while ( 1 ) {
421         $Conf{CgiImageDirURL} = prompt("--> URL for image directory (omit http://host; starts with '/')",
422                                         $Conf{CgiImageDirURL},
423                                         "html-dir-url");
424         last if ( $Conf{CgiImageDirURL} =~ /^\// );
425         if ( $opts{batch} ) {
426             print("Need to specify --html-dir-url for new installation\n");
427             exit(1);
428         }
429     }
430 }
431
432 print <<EOF;
433
434 Ok, we're about to:
435
436   - install the binaries, lib and docs in $Conf{InstallDir},
437   - create the data directory $Conf{TopDir},
438   - create/update the config.pl file $Conf{TopDir}/conf,
439   - optionally install the cgi-bin interface.
440
441 EOF
442
443 exit unless prompt("--> Do you want to continue?", "y") =~ /y/i;
444
445 #
446 # Create install directories
447 #
448 foreach my $dir ( qw(bin doc
449                      lib/BackupPC/CGI
450                      lib/BackupPC/Config
451                      lib/BackupPC/Lang
452                      lib/BackupPC/Storage
453                      lib/BackupPC/Xfer
454                      lib/BackupPC/Zip
455                  ) ) {
456     next if ( -d "$DestDir$Conf{InstallDir}/$dir" );
457     mkpath("$DestDir$Conf{InstallDir}/$dir", 0, 0775);
458     if ( !-d "$DestDir$Conf{InstallDir}/$dir"
459             || !chown($Uid, $Gid, "$DestDir$Conf{InstallDir}/$dir") ) {
460         die("Failed to create or chown $DestDir$Conf{InstallDir}/$dir\n");
461     } else {
462         print("Created $DestDir$Conf{InstallDir}/$dir\n");
463     }
464 }
465
466 #
467 # Create CGI image directory
468 #
469 foreach my $dir ( ($Conf{CgiImageDir}) ) {
470     next if ( $dir eq "" || -d $dir );
471     mkpath("$DestDir$dir", 0, 0775);
472     if ( !-d "$DestDir$dir" || !chown($Uid, $Gid, "$DestDir$dir") ) {
473         die("Failed to create or chown $DestDir$dir");
474     } else {
475         print("Created $DestDir$dir\n");
476     }
477 }
478
479 #
480 # Create $TopDir's top-level directories
481 #
482 foreach my $dir ( qw(. conf pool cpool pc trash log) ) {
483     mkpath("$DestDir$Conf{TopDir}/$dir", 0, 0750) if ( !-d "$DestDir$Conf{TopDir}/$dir" );
484     if ( !-d "$DestDir$Conf{TopDir}/$dir"
485             || !chown($Uid, $Gid, "$DestDir$Conf{TopDir}/$dir") ) {
486         die("Failed to create or chown $DestDir$Conf{TopDir}/$dir\n");
487     } else {
488         print("Created $DestDir$Conf{TopDir}/$dir\n");
489     }
490 }
491
492 printf("Installing binaries in $DestDir$Conf{InstallDir}/bin\n");
493 foreach my $prog ( qw(BackupPC BackupPC_dump BackupPC_link BackupPC_nightly
494         BackupPC_sendEmail BackupPC_tarCreate BackupPC_trashClean
495         BackupPC_tarExtract BackupPC_compressPool BackupPC_zcat
496         BackupPC_archive BackupPC_archiveHost
497         BackupPC_restore BackupPC_serverMesg BackupPC_zipCreate ) ) {
498     InstallFile("bin/$prog", "$DestDir$Conf{InstallDir}/bin/$prog", 0555);
499 }
500
501 printf("Installing library in $DestDir$Conf{InstallDir}/lib\n");
502 foreach my $lib ( qw(
503         BackupPC/Lib.pm
504         BackupPC/Attrib.pm
505         BackupPC/FileZIO.pm
506         BackupPC/Config.pm
507         BackupPC/PoolWrite.pm
508         BackupPC/Storage.pm
509         BackupPC/View.pm
510         BackupPC/CGI/AdminOptions.pm
511         BackupPC/CGI/Archive.pm
512         BackupPC/CGI/ArchiveInfo.pm
513         BackupPC/CGI/Browse.pm
514         BackupPC/CGI/DirHistory.pm
515         BackupPC/CGI/EmailSummary.pm
516         BackupPC/CGI/GeneralInfo.pm
517         BackupPC/CGI/HostInfo.pm
518         BackupPC/CGI/Lib.pm
519         BackupPC/CGI/LOGlist.pm
520         BackupPC/CGI/Queue.pm
521         BackupPC/CGI/ReloadServer.pm
522         BackupPC/CGI/RestoreFile.pm
523         BackupPC/CGI/RestoreInfo.pm
524         BackupPC/CGI/Restore.pm
525         BackupPC/CGI/StartServer.pm
526         BackupPC/CGI/StartStopBackup.pm
527         BackupPC/CGI/StopServer.pm
528         BackupPC/CGI/Summary.pm
529         BackupPC/CGI/View.pm
530         BackupPC/Config/Meta.pm
531         BackupPC/Lang/en.pm
532         BackupPC/Lang/fr.pm
533         BackupPC/Lang/es.pm
534         BackupPC/Lang/de.pm
535         BackupPC/Lang/it.pm
536         BackupPC/Lang/nl.pm
537         BackupPC/Lang/pt_br.pm
538         BackupPC/Storage/Text.pm
539         BackupPC/Xfer/Archive.pm
540         BackupPC/Xfer/Tar.pm
541         BackupPC/Xfer/Smb.pm
542         BackupPC/Xfer/Rsync.pm
543         BackupPC/Xfer/RsyncDigest.pm
544         BackupPC/Xfer/RsyncFileIO.pm
545         BackupPC/Zip/FileMember.pm
546     ) ) {
547     InstallFile("lib/$lib", "$DestDir$Conf{InstallDir}/lib/$lib", 0444);
548 }
549
550 if ( $Conf{CgiImageDir} ne "" ) {
551     printf("Installing images in $DestDir$Conf{CgiImageDir}\n");
552     foreach my $img ( <images/*> ) {
553         (my $destImg = $img) =~ s{^images/}{};
554         InstallFile($img, "$DestDir$Conf{CgiImageDir}/$destImg", 0444, 1);
555     }
556
557     #
558     # Install new CSS file, making a backup copy if necessary
559     #
560     my $cssBackup = "$DestDir$Conf{CgiImageDir}/BackupPC_stnd.css.pre-__VERSION__";
561     if ( -f "$DestDir$Conf{CgiImageDir}/BackupPC_stnd.css" && !-f $cssBackup ) {
562         rename("$DestDir$Conf{CgiImageDir}/BackupPC_stnd.css", $cssBackup);
563     }
564     InstallFile("conf/BackupPC_stnd.css",
565                 "$DestDir$Conf{CgiImageDir}/BackupPC_stnd.css", 0444, 0);
566 }
567
568 printf("Making init.d scripts\n");
569 foreach my $init ( qw(gentoo-backuppc gentoo-backuppc.conf linux-backuppc
570                       solaris-backuppc debian-backuppc suse-backuppc
571                       slackware-backuppc ) ) {
572     InstallFile("init.d/src/$init", "init.d/$init", 0444);
573 }
574
575 printf("Installing docs in $DestDir$Conf{InstallDir}/doc\n");
576 foreach my $doc ( qw(BackupPC.pod BackupPC.html) ) {
577     InstallFile("doc/$doc", "$DestDir$Conf{InstallDir}/doc/$doc", 0444);
578 }
579
580 printf("Installing config.pl and hosts in $DestDir$Conf{TopDir}/conf\n");
581 InstallFile("conf/hosts", "$DestDir$Conf{TopDir}/conf/hosts", 0644)
582                     if ( !-f "$DestDir$Conf{TopDir}/conf/hosts" );
583
584 #
585 # Now do the config file.  If there is an existing config file we
586 # merge in the new config file, adding any new configuration
587 # parameters and deleting ones that are no longer needed.
588 #
589 my $dest = "$DestDir$Conf{TopDir}/conf/config.pl";
590 my ($newConf, $newVars) = ConfigParse("conf/config.pl");
591 my ($oldConf, $oldVars);
592 if ( -f $dest ) {
593     ($oldConf, $oldVars) = ConfigParse($dest);
594     $newConf = ConfigMerge($oldConf, $oldVars, $newConf, $newVars);
595 }
596 $Conf{EMailFromUserName}  ||= $Conf{BackupPCUser};
597 $Conf{EMailAdminUserName} ||= $Conf{BackupPCUser};
598
599 #
600 # Update various config parameters
601 #
602
603 #
604 # Guess $Conf{CgiURL}
605 #
606 if ( !defined($Conf{CgiURL}) ) {
607     if ( $Conf{CgiDir} =~ m{cgi-bin(/.*)} ) {
608         $Conf{CgiURL} = "'http://$Conf{ServerHost}/cgi-bin$1/BackupPC_Admin'";
609     } else {
610         $Conf{CgiURL} = "'http://$Conf{ServerHost}/cgi-bin/BackupPC_Admin'";
611     }
612 }
613
614 #
615 # The smbclient commands have moved from hard-coded to the config file.
616 # $Conf{SmbClientArgs} no longer exists, so merge it into the new
617 # commands if it still exists.
618 #
619 if ( defined($Conf{SmbClientArgs}) ) {
620     if ( $Conf{SmbClientArgs} ne "" ) {
621         foreach my $param ( qw(SmbClientRestoreCmd SmbClientFullCmd
622                                 SmbClientIncrCmd) ) {
623             $newConf->[$newVars->{$param}]{text}
624                             =~ s/(-E\s+-N)/$1 $Conf{SmbClientArgs}/;
625         }
626     }
627     delete($Conf{SmbClientArgs});
628 }
629
630 #
631 # CSS is now stored in a file rather than a big config variable.
632 #
633 delete($Conf{CSSstylesheet});
634
635 #
636 # The blackout timing settings are now stored in a list of hashes, rather
637 # than three scalar parameters.
638 #
639 if ( defined($Conf{BlackoutHourBegin}) ) {
640     $Conf{BlackoutPeriods} = [
641          {
642              hourBegin => $Conf{BlackoutHourBegin},
643              hourEnd   => $Conf{BlackoutHourEnd},
644              weekDays  => $Conf{BlackoutWeekDays},
645          } 
646     ];
647     delete($Conf{BlackoutHourBegin});
648     delete($Conf{BlackoutHourEnd});
649     delete($Conf{BlackoutWeekDays});
650 }
651
652 #
653 # $Conf{RsyncLogLevel} has been replaced by $Conf{XferLogLevel}
654 #
655 if ( defined($Conf{RsyncLogLevel}) ) {
656     $Conf{XferLogLevel} = $Conf{RsyncLogLevel};
657     delete($Conf{RsyncLogLevel});
658 }
659
660 #
661 # In 2.1.0 the default for $Conf{CgiNavBarAdminAllHosts} is now 1
662 #
663 $Conf{CgiNavBarAdminAllHosts} = 1;
664
665 #
666 # IncrFill should now be off
667 #
668 $Conf{IncrFill} = 0;
669
670 #
671 # Figure out sensible arguments for the ping command
672 #
673 if ( defined($Conf{PingArgs}) ) {
674     $Conf{PingCmd} = '$pingPath ' . $Conf{PingArgs};
675 } elsif ( !defined($Conf{PingCmd}) ) {
676     if ( $^O eq "solaris" || $^O eq "sunos" ) {
677         $Conf{PingCmd} = '$pingPath -s $host 56 1';
678     } elsif ( ($^O eq "linux" || $^O eq "openbsd" || $^O eq "netbsd")
679             && !system("$Conf{PingPath} -c 1 -w 3 localhost") ) {
680         $Conf{PingCmd} = '$pingPath -c 1 -w 3 $host';
681     } else {
682         $Conf{PingCmd} = '$pingPath -c 1 $host';
683     }
684     delete($Conf{PingArgs});
685 }
686
687 #
688 # Figure out sensible arguments for the df command
689 #
690 if ( !defined($Conf{DfCmd}) ) {
691     if ( $^O eq "solaris" || $^O eq "sunos" ) {
692         $Conf{DfCmd} = '$dfPath -k $topDir';
693     }
694 }
695
696 #
697 # $Conf{SmbClientTimeout} is now $Conf{ClientTimeout}
698 #
699 if ( defined($Conf{SmbClientTimeout}) ) {
700     $Conf{ClientTimeout} = $Conf{SmbClientTimeout};
701     delete($Conf{SmbClientTimeout});
702 }
703
704 my $confCopy = "$dest.pre-__VERSION__";
705 if ( -f $dest && !-f $confCopy ) {
706     #
707     # Make copy of config file, preserving ownership and modes
708     #
709     printf("Making backup copy of $dest -> $confCopy\n");
710     my @stat = stat($dest);
711     my $mode = $stat[2];
712     my $uid  = $stat[4];
713     my $gid  = $stat[5];
714     die("can't copy($dest, $confCopy)\n")  unless copy($dest, $confCopy);
715     die("can't chown $uid, $gid $confCopy\n")
716                                            unless chown($uid, $gid, $confCopy);
717     die("can't chmod $mode $confCopy\n")   unless chmod($mode, $confCopy);
718 }
719 open(OUT, ">", $dest) || die("can't open $dest for writing\n");
720 binmode(OUT);
721 my $blockComment;
722 foreach my $var ( @$newConf ) {
723     if ( length($blockComment)
724           && substr($var->{text}, 0, length($blockComment)) eq $blockComment ) {
725         $var->{text} = substr($var->{text}, length($blockComment));
726         $blockComment = undef;
727     }
728     $blockComment = $1 if ( $var->{text} =~ /^([\s\n]*#{70}.*#{70}[\s\n]+)/s );
729     $var->{text} =~ s/^\s*\$Conf\{(.*?)\}(\s*=\s*['"]?)(.*?)(['"]?\s*;)/
730                 defined($Conf{$1}) && ref($Conf{$1}) eq ""
731                                    && $Conf{$1} ne $OrigConf{$1}
732                                    ? "\$Conf{$1}$2$Conf{$1}$4"
733                                    : "\$Conf{$1}$2$3$4"/emg;
734     print OUT $var->{text};
735 }
736 close(OUT);
737 if ( !defined($oldConf) ) {
738     die("can't chmod 0640 mode $dest\n")  unless chmod(0640, $dest);
739     die("can't chown $Uid, $Gid $dest\n") unless chown($Uid, $Gid, $dest);
740 }
741
742 if ( $Conf{CgiDir} ne "" ) {
743     printf("Installing cgi script BackupPC_Admin in $DestDir$Conf{CgiDir}\n");
744     mkpath("$DestDir$Conf{CgiDir}", 0, 0755);
745     InstallFile("cgi-bin/BackupPC_Admin", "$DestDir$Conf{CgiDir}/BackupPC_Admin",
746                 04554);
747 }
748
749 print <<EOF;
750
751 Ok, it looks like we are finished.  There are several more things you
752 will need to do:
753
754   - Browse through the config file, $Conf{TopDir}/conf/config.pl,
755     and make sure all the settings are correct.  In particular, you
756     will need to set the smb share password and user name, backup
757     policies and check the email message headers and bodies.
758
759   - Edit the list of hosts to backup in $Conf{TopDir}/conf/hosts.
760
761   - Read the documentation in $Conf{InstallDir}/doc/BackupPC.html.
762     Please pay special attention to the security section.
763
764   - Verify that the CGI script BackupPC_Admin runs correctly.  You might
765     need to change the permissions or group ownership of BackupPC_Admin.
766
767   - BackupPC should be ready to start.  Don't forget to run it
768     as user $Conf{BackupPCUser}!  The installation also contains an
769     init.d/backuppc script that can be copied to /etc/init.d
770     so that BackupPC can auto-start on boot.  This will also enable
771     administrative users to start the server from the CGI interface.
772     See init.d/README.
773
774 Enjoy!
775 EOF
776
777 if ( `$Conf{PerlPath} -V` =~ /uselargefiles=undef/ ) {
778     print <<EOF;
779
780 Warning: your perl, $Conf{PerlPath}, does not support large files.
781 This means BackupPC won't be able to backup files larger than 2GB.
782 To solve this problem you should build/install a new version of perl
783 with large file support enabled.  Use
784
785     $Conf{PerlPath} -V | egrep uselargefiles
786
787 to check if perl has large file support (undef means no support).
788 EOF
789 }
790
791 eval "use File::RsyncP;";
792 if ( !$@ && $File::RsyncP::VERSION < 0.51 ) {
793     print("\nWarning: you need to upgrade File::RsyncP;"
794         . " I found $File::RsyncP::VERSION and BackupPC needs 0.51\n");
795 }
796
797 exit(0);
798
799 ###########################################################################
800 # Subroutines
801 ###########################################################################
802
803 sub InstallFile
804 {
805     my($prog, $dest, $mode, $binary) = @_;
806     my $first = 1;
807     my($uid, $gid) = ($Uid, $Gid);
808
809     if ( -f $dest ) {
810         #
811         # preserve ownership and modes of files that already exist
812         #
813         my @stat = stat($dest);
814         $mode = $stat[2];
815         $uid  = $stat[4];
816         $gid  = $stat[5];
817     }
818     unlink($dest) if ( -f $dest );
819     if ( $binary ) {
820         die("can't copy($prog, $dest)\n") unless copy($prog, $dest);
821     } else {
822         open(PROG, $prog)     || die("can't open $prog for reading\n");
823         open(OUT, ">", $dest) || die("can't open $dest for writing\n");
824         binmode(PROG);
825         binmode(OUT);
826         while ( <PROG> ) {
827             s/__INSTALLDIR__/$Conf{InstallDir}/g;
828             s/__TOPDIR__/$Conf{TopDir}/g;
829             s/__BACKUPPCUSER__/$Conf{BackupPCUser}/g;
830             s/__CGIDIR__/$Conf{CgiDir}/g;
831             if ( $first && /^#.*bin\/perl/ ) {
832                 #
833                 # Fill in correct path to perl (no taint for >= 2.0.1).
834                 #
835                 print OUT "#!$Conf{PerlPath}\n";
836             } else {
837                 print OUT;
838             }
839             $first = 0;
840         }
841         close(PROG);
842         close(OUT);
843     }
844     die("can't chown $uid, $gid $dest") unless chown($uid, $gid, $dest);
845     die("can't chmod $mode $dest")      unless chmod($mode, $dest);
846 }
847
848 sub FindProgram
849 {
850     my($path, $prog) = @_;
851
852     if ( defined($opts{"bin-path"}{$prog}) ) {
853         return $opts{"bin-path"}{$prog};
854     }
855     foreach my $dir ( split(/:/, $path) ) {
856         my $file = File::Spec->catfile($dir, $prog);
857         return $file if ( -x $file );
858     }
859     return;
860 }
861
862 sub ConfigParse
863 {
864     my($file) = @_;
865     open(C, $file) || die("can't open $file");
866     binmode(C);
867     my($out, @conf, $var);
868     my $comment = 1;
869     my $allVars = {};
870     my $endLine = undef;
871     while ( <C> ) {
872         if ( /^#/ && !defined($endLine) ) {
873             if ( $comment ) {
874                 $out .= $_;
875             } else {
876                 if ( $out ne "" ) {
877                     $allVars->{$var} = @conf if ( defined($var) );
878                     push(@conf, {
879                         text => $out,
880                         var => $var,
881                     });
882                 }
883                 $var = undef;
884                 $comment = 1;
885                 $out = $_;
886             }
887         } elsif ( /^\s*\$Conf\{([^}]*)/ ) {
888             $comment = 0;
889             if ( defined($var) ) {
890                 $allVars->{$var} = @conf if ( defined($var) );
891                 push(@conf, {
892                     text => $out,
893                     var => $var,
894                 });
895                 $out = $_;
896             } else {
897                 $out .= $_;
898             }
899             $var = $1;
900             $endLine = $1 if ( /^\s*\$Conf\{[^}]*} *= *<<(.*);/ );
901             $endLine = $1 if ( /^\s*\$Conf\{[^}]*} *= *<<'(.*)';/ );
902         } else {
903             $endLine = undef if ( defined($endLine) && /^\Q$endLine[\n\r]*$/ );
904             $out .= $_;
905         }
906     }
907     if ( $out ne "" ) {
908         $allVars->{$var} = @conf if ( defined($var) );
909         push(@conf, {
910             text => $out,
911             var  => $var,
912         });
913     }
914     close(C);
915     return (\@conf, $allVars);
916 }
917
918 sub ConfigMerge
919 {
920     my($old, $oldVars, $new, $newVars) = @_;
921     my $posn = 0;
922     my $res;
923
924     #
925     # Find which config parameters are not needed any longer
926     #
927     foreach my $var ( @$old ) {
928         next if ( !defined($var->{var}) || defined($newVars->{$var->{var}}) );
929         #print(STDERR "Deleting old config parameter $var->{var}\n");
930         $var->{delete} = 1;
931     }
932     #
933     # Find which config parameters are new
934     #
935     foreach my $var ( @$new ) {
936         next if ( !defined($var->{var}) );
937         if ( defined($oldVars->{$var->{var}}) ) {
938             $posn = $oldVars->{$var->{var}};
939         } else {
940             #print(STDERR "New config parameter $var->{var}: $var->{text}\n");
941             push(@{$old->[$posn]{new}}, $var);
942         }
943     }
944     #
945     # Create merged config file
946     #
947     foreach my $var ( @$old ) {
948         next if ( $var->{delete} );
949         push(@$res, $var);
950         foreach my $new ( @{$var->{new}} ) {
951             push(@$res, $new);
952         }
953     }
954     return $res;
955 }
956
957 sub prompt
958 {
959     my($question, $default, $option) = @_;
960
961     $default = $opts{$option} if ( defined($opts{$option}) );
962     if ( $opts{batch} ) {
963         print("$question [$default]\n");
964         return $default;
965     }
966     print("$question [$default]? ");
967     my $reply = <STDIN>;
968     $reply =~ s/[\n\r]*//g;
969     return $reply if ( $reply !~ /^$/ );
970     return $default;
971 }
972
973 __END__
974
975 =head1 SYNOPSIS
976
977 configure.pl [options]
978
979 =head1 DESCRIPTION
980
981 configure.pl is a script that is used to install or upgrade a BackupPC
982 installation.  It is usually run interactively without arguments.  It
983 also supports a batch mode where all the options can be specified
984 via the command-line.
985
986 For upgrading BackupPC you need to make sure that BackupPC is not
987 running prior to running BackupPC.
988
989 Typically configure.pl needs to run as the super user (root).
990
991 =head1 OPTIONS
992
993 =over 8
994
995 =item B<--batch>
996
997 Run configure.pl in batch mode.  configure.pl will run without
998 prompting the user.  The other command-line options are used
999 to specify the settings that the user is usually prompted for.
1000
1001 =item B<--bin-path PROG=PATH>
1002
1003 Specify the path for various external programs that BackupPC
1004 uses.  Several --bin-path options may be specified.  configure.pl
1005 usually finds sensible defaults based on searching the PATH.
1006 The format is:
1007
1008     --bin-path PROG=PATH
1009
1010 where PROG is one of perl, tar, smbclient, nmblookup, rsync, ping,
1011 df, ssh, sendmail, hostname, split, par2, cat, gzip, bzip2 and
1012 PATH is that full path to that program.
1013
1014 Examples
1015
1016     --bin-path cat=/bin/cat --bin-path bzip2=/home/user/bzip2
1017
1018 =item B<--config-path CONFIG_PATH>
1019
1020 Path to the existing config.pl configuration file for BackupPC.
1021 This option should be specified for batch upgrades to an
1022 existing installation.  The option should be omitted when
1023 doing a batch new install.
1024
1025 =item B<--cgi-dir CGI_DIR>
1026
1027 Path to Apache's cgi-bin directory where the BackupPC_Admin
1028 script will be installed.  This option only needs to be
1029 specified for a batch new install.
1030
1031 =item B<--data-dir DATA_DIR>
1032
1033 Path to the BackupPC data directory.  This is where all the backup
1034 data is stored, and it should be on a large file system. This option
1035 only needs to be specified for a batch new install.
1036
1037 Example:
1038
1039     --data-dir /data/BackupPC
1040
1041 =item B<--dest-dir DEST_DIR>
1042
1043 An optional prefix to apply to all installation directories.
1044 Usually this is not needed, but certain auto-installers like
1045 to stage an install in a temporary directory, and then copy
1046 the files to their real destination.  This option can be used
1047 to specify the temporary directory prefix.  Note that if you
1048 specify this option, BackupPC won't run correctly if you try
1049 to run it from below the --dest-dir directory, since all the
1050 paths are set assuming BackupPC is installed in the intended
1051 final locations.
1052
1053 =item B<--help|?>
1054
1055 Print a brief help message and exits.
1056
1057 =item B<--hostname HOSTNAME>
1058
1059 Host name (this machine's name) on which BackupPC is being installed.
1060 This option only needs to be specified for a batch new install.
1061
1062 =item B<--html-dir HTML_DIR>
1063
1064 Path to an Apache html directory where various BackupPC image files
1065 and the CSS files will be installed.  This is typically a directory
1066 below Apache's DocumentRoot directory.  This option only needs to be
1067 specified for a batch new install.
1068
1069 Example:
1070
1071     --html-dir /usr/local/apache/htdocs/BackupPC
1072
1073 =item B<--html-dir-url URL>
1074
1075 The URL (without http://hostname) required to access the BackupPC html
1076 directory specified with the --html-dir option.  This option only needs
1077 to be specified for a batch new install.
1078
1079 Example:
1080
1081     --html-dir-url /BackupPC
1082
1083 =item B<--install-dir INSTALL_DIR>
1084
1085 Installation directory for BackupPC scripts, libraries, and
1086 documentation.  This option only needs to be specified for a
1087 batch new install.
1088
1089 Example:
1090
1091     --install-dir /usr/local/BackupPC
1092
1093 =item B<--man>
1094
1095 Prints the manual page and exits.
1096
1097 =item B<--uid-ignore>
1098
1099 configure.pl verifies that the script is being run as the super user
1100 (root).  Without the --uid-ignore option, in batch mode the script will
1101 exit with an error if not run as the super user, and in interactive mode
1102 the user will be prompted.  Specifying this option will cause the script
1103 to continue even if the user id is not root.
1104
1105 =head1 EXAMPLES
1106
1107 For a standard interactive install, run without arguments:
1108
1109     configure.pl
1110
1111 For a batch new install you need to specify answers to all the
1112 questions that are normally prompted:
1113
1114     configure.pl                                   \
1115         --batch                                    \
1116         --cgi-dir /var/www/cgi-bin/BackupPC        \
1117         --data-dir /data/BackupPC                  \
1118         --hostname myHost                          \
1119         --html-dir /var/www/html/BackupPC          \
1120         --html-dir-url /BackupPC                   \
1121         --install-dir /usr/local/BackupPC
1122
1123 For a batch upgrade, you only need to specify the path to the
1124 configuration file:
1125         
1126     configure.pl --batch --config-path /data/BackupPC/conf/config.pl
1127
1128 =head1 AUTHOR
1129
1130 Craig Barratt <cbarratt@users.sourceforge.net>
1131
1132 =head1 COPYRIGHT
1133
1134 Copyright (C) 2001-2004  Craig Barratt.
1135
1136 This program is free software; you can redistribute it and/or modify
1137 it under the terms of the GNU General Public License as published by
1138 the Free Software Foundation; either version 2 of the License, or
1139 (at your option) any later version.
1140
1141 =cut