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