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