- couple of fixes for configure.pl and makeDist
[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(
552         __CONFIGURE_BIN_LIST__
553     ) ) {
554     InstallFile($prog, "$DestDir$Conf{InstallDir}/$prog", 0555);
555 }
556
557 printf("Installing library in $DestDir$Conf{InstallDir}/lib\n");
558 foreach my $lib ( qw(
559         __CONFIGURE_LIB_LIST__
560     ) ) {
561     InstallFile($lib, "$DestDir$Conf{InstallDir}/$lib", 0444);
562 }
563
564 if ( $Conf{CgiImageDir} ne "" ) {
565     printf("Installing images in $DestDir$Conf{CgiImageDir}\n");
566     foreach my $img ( <images/*> ) {
567         (my $destImg = $img) =~ s{^images/}{};
568         InstallFile($img, "$DestDir$Conf{CgiImageDir}/$destImg", 0444, 1);
569     }
570
571     #
572     # Install new CSS file, making a backup copy if necessary
573     #
574     my $cssBackup = "$DestDir$Conf{CgiImageDir}/BackupPC_stnd.css.pre-__VERSION__";
575     if ( -f "$DestDir$Conf{CgiImageDir}/BackupPC_stnd.css" && !-f $cssBackup ) {
576         rename("$DestDir$Conf{CgiImageDir}/BackupPC_stnd.css", $cssBackup);
577     }
578     InstallFile("conf/BackupPC_stnd.css",
579                 "$DestDir$Conf{CgiImageDir}/BackupPC_stnd.css", 0444, 0);
580 }
581
582 printf("Making init.d scripts\n");
583 foreach my $init ( qw(gentoo-backuppc gentoo-backuppc.conf linux-backuppc
584                       solaris-backuppc debian-backuppc suse-backuppc
585                       slackware-backuppc ) ) {
586     InstallFile("init.d/src/$init", "init.d/$init", 0444);
587 }
588
589 printf("Installing docs in $DestDir$Conf{InstallDir}/doc\n");
590 foreach my $doc ( qw(BackupPC.pod BackupPC.html) ) {
591     InstallFile("doc/$doc", "$DestDir$Conf{InstallDir}/doc/$doc", 0444);
592 }
593
594 printf("Installing config.pl and hosts in $DestDir$Conf{ConfDir}\n");
595 InstallFile("conf/hosts", "$DestDir$Conf{ConfDir}/hosts", 0644)
596                     if ( !-f "$DestDir$Conf{ConfDir}/hosts" );
597
598 #
599 # Now do the config file.  If there is an existing config file we
600 # merge in the new config file, adding any new configuration
601 # parameters and deleting ones that are no longer needed.
602 #
603 my $dest = "$DestDir$Conf{ConfDir}/config.pl";
604 my ($newConf, $newVars) = ConfigParse("conf/config.pl");
605 my ($oldConf, $oldVars);
606 if ( -f $dest ) {
607     ($oldConf, $oldVars) = ConfigParse($dest);
608     $newConf = ConfigMerge($oldConf, $oldVars, $newConf, $newVars);
609 }
610 $Conf{EMailFromUserName}  ||= $Conf{BackupPCUser};
611 $Conf{EMailAdminUserName} ||= $Conf{BackupPCUser};
612
613 #
614 # Update various config parameters
615 #
616
617 #
618 # Guess $Conf{CgiURL}
619 #
620 if ( !defined($Conf{CgiURL}) ) {
621     if ( $Conf{CgiDir} =~ m{cgi-bin(/.*)} ) {
622         $Conf{CgiURL} = "'http://$Conf{ServerHost}/cgi-bin$1/BackupPC_Admin'";
623     } else {
624         $Conf{CgiURL} = "'http://$Conf{ServerHost}/cgi-bin/BackupPC_Admin'";
625     }
626 }
627
628 #
629 # The smbclient commands have moved from hard-coded to the config file.
630 # $Conf{SmbClientArgs} no longer exists, so merge it into the new
631 # commands if it still exists.
632 #
633 if ( defined($Conf{SmbClientArgs}) ) {
634     if ( $Conf{SmbClientArgs} ne "" ) {
635         foreach my $param ( qw(SmbClientRestoreCmd SmbClientFullCmd
636                                 SmbClientIncrCmd) ) {
637             $newConf->[$newVars->{$param}]{text}
638                             =~ s/(-E\s+-N)/$1 $Conf{SmbClientArgs}/;
639         }
640     }
641     delete($Conf{SmbClientArgs});
642 }
643
644 #
645 # CSS is now stored in a file rather than a big config variable.
646 #
647 delete($Conf{CSSstylesheet});
648
649 #
650 # The blackout timing settings are now stored in a list of hashes, rather
651 # than three scalar parameters.
652 #
653 if ( defined($Conf{BlackoutHourBegin}) ) {
654     $Conf{BlackoutPeriods} = [
655          {
656              hourBegin => $Conf{BlackoutHourBegin},
657              hourEnd   => $Conf{BlackoutHourEnd},
658              weekDays  => $Conf{BlackoutWeekDays},
659          } 
660     ];
661     delete($Conf{BlackoutHourBegin});
662     delete($Conf{BlackoutHourEnd});
663     delete($Conf{BlackoutWeekDays});
664 }
665
666 #
667 # $Conf{RsyncLogLevel} has been replaced by $Conf{XferLogLevel}
668 #
669 if ( defined($Conf{RsyncLogLevel}) ) {
670     $Conf{XferLogLevel} = $Conf{RsyncLogLevel};
671     delete($Conf{RsyncLogLevel});
672 }
673
674 #
675 # In 2.1.0 the default for $Conf{CgiNavBarAdminAllHosts} is now 1
676 #
677 $Conf{CgiNavBarAdminAllHosts} = 1;
678
679 #
680 # IncrFill should now be off
681 #
682 $Conf{IncrFill} = 0;
683
684 #
685 # Figure out sensible arguments for the ping command
686 #
687 if ( defined($Conf{PingArgs}) ) {
688     $Conf{PingCmd} = '$pingPath ' . $Conf{PingArgs};
689 } elsif ( !defined($Conf{PingCmd}) ) {
690     if ( $^O eq "solaris" || $^O eq "sunos" ) {
691         $Conf{PingCmd} = '$pingPath -s $host 56 1';
692     } elsif ( ($^O eq "linux" || $^O eq "openbsd" || $^O eq "netbsd")
693             && !system("$Conf{PingPath} -c 1 -w 3 localhost") ) {
694         $Conf{PingCmd} = '$pingPath -c 1 -w 3 $host';
695     } else {
696         $Conf{PingCmd} = '$pingPath -c 1 $host';
697     }
698     delete($Conf{PingArgs});
699 }
700
701 #
702 # Figure out sensible arguments for the df command
703 #
704 if ( !defined($Conf{DfCmd}) ) {
705     if ( $^O eq "solaris" || $^O eq "sunos" ) {
706         $Conf{DfCmd} = '$dfPath -k $topDir';
707     }
708 }
709
710 #
711 # $Conf{SmbClientTimeout} is now $Conf{ClientTimeout}
712 #
713 if ( defined($Conf{SmbClientTimeout}) ) {
714     $Conf{ClientTimeout} = $Conf{SmbClientTimeout};
715     delete($Conf{SmbClientTimeout});
716 }
717
718 my $confCopy = "$dest.pre-__VERSION__";
719 if ( -f $dest && !-f $confCopy ) {
720     #
721     # Make copy of config file, preserving ownership and modes
722     #
723     printf("Making backup copy of $dest -> $confCopy\n");
724     my @stat = stat($dest);
725     my $mode = $stat[2];
726     my $uid  = $stat[4];
727     my $gid  = $stat[5];
728     die("can't copy($dest, $confCopy)\n")
729                                 unless copy($dest, $confCopy);
730     die("can't chown $uid, $gid $confCopy\n")
731                                 unless my_chown($uid, $gid, $confCopy);
732     die("can't chmod $mode $confCopy\n")
733                                 unless my_chmod($mode, $confCopy);
734 }
735 open(OUT, ">", $dest) || die("can't open $dest for writing\n");
736 binmode(OUT);
737 my $blockComment;
738 foreach my $var ( @$newConf ) {
739     if ( length($blockComment)
740           && substr($var->{text}, 0, length($blockComment)) eq $blockComment ) {
741         $var->{text} = substr($var->{text}, length($blockComment));
742         $blockComment = undef;
743     }
744     $blockComment = $1 if ( $var->{text} =~ /^([\s\n]*#{70}.*#{70}[\s\n]+)/s );
745     $var->{text} =~ s/^\s*\$Conf\{(.*?)\}(\s*=\s*['"]?)(.*?)(['"]?\s*;)/
746                 defined($Conf{$1}) && ref($Conf{$1}) eq ""
747                                    && $Conf{$1} ne $OrigConf{$1}
748                                    ? "\$Conf{$1}$2$Conf{$1}$4"
749                                    : "\$Conf{$1}$2$3$4"/emg;
750     print OUT $var->{text};
751 }
752 close(OUT);
753 if ( !defined($oldConf) ) {
754     die("can't chmod 0640 mode $dest\n")  unless my_chmod(0640, $dest);
755     die("can't chown $Uid, $Gid $dest\n") unless my_chown($Uid, $Gid, $dest);
756 }
757
758 if ( $Conf{CgiDir} ne "" ) {
759     printf("Installing cgi script BackupPC_Admin in $DestDir$Conf{CgiDir}\n");
760     mkpath("$DestDir$Conf{CgiDir}", 0, 0755);
761     InstallFile("cgi-bin/BackupPC_Admin", "$DestDir$Conf{CgiDir}/BackupPC_Admin",
762                 04554);
763 }
764
765 print <<EOF;
766
767 Ok, it looks like we are finished.  There are several more things you
768 will need to do:
769
770   - Browse through the config file, $Conf{ConfDir}/config.pl,
771     and make sure all the settings are correct.  In particular, you
772     will need to set the smb share password and user name, backup
773     policies and check the email message headers and bodies.
774
775   - Edit the list of hosts to backup in $Conf{ConfDir}/hosts.
776
777   - Read the documentation in $Conf{InstallDir}/doc/BackupPC.html.
778     Please pay special attention to the security section.
779
780   - Verify that the CGI script BackupPC_Admin runs correctly.  You might
781     need to change the permissions or group ownership of BackupPC_Admin.
782
783   - BackupPC should be ready to start.  Don't forget to run it
784     as user $Conf{BackupPCUser}!  The installation also contains an
785     init.d/backuppc script that can be copied to /etc/init.d
786     so that BackupPC can auto-start on boot.  This will also enable
787     administrative users to start the server from the CGI interface.
788     See init.d/README.
789
790 Enjoy!
791 EOF
792
793 if ( `$Conf{PerlPath} -V` =~ /uselargefiles=undef/ ) {
794     print <<EOF;
795
796 Warning: your perl, $Conf{PerlPath}, does not support large files.
797 This means BackupPC won't be able to backup files larger than 2GB.
798 To solve this problem you should build/install a new version of perl
799 with large file support enabled.  Use
800
801     $Conf{PerlPath} -V | egrep uselargefiles
802
803 to check if perl has large file support (undef means no support).
804 EOF
805 }
806
807 eval "use File::RsyncP;";
808 if ( !$@ && $File::RsyncP::VERSION < 0.52 ) {
809     print("\nWarning: you need to upgrade File::RsyncP;"
810         . " I found $File::RsyncP::VERSION and BackupPC needs 0.52\n");
811 }
812
813 exit(0);
814
815 ###########################################################################
816 # Subroutines
817 ###########################################################################
818
819 sub InstallFile
820 {
821     my($prog, $dest, $mode, $binary) = @_;
822     my $first = 1;
823     my($uid, $gid) = ($Uid, $Gid);
824
825     if ( -f $dest ) {
826         #
827         # preserve ownership and modes of files that already exist
828         #
829         my @stat = stat($dest);
830         $mode = $stat[2];
831         $uid  = $stat[4];
832         $gid  = $stat[5];
833     }
834     unlink($dest) if ( -f $dest );
835     if ( $binary ) {
836         die("can't copy($prog, $dest)\n") unless copy($prog, $dest);
837     } else {
838         open(PROG, $prog)     || die("can't open $prog for reading\n");
839         open(OUT, ">", $dest) || die("can't open $dest for writing\n");
840         binmode(PROG);
841         binmode(OUT);
842         while ( <PROG> ) {
843             s/__INSTALLDIR__/$Conf{InstallDir}/g;
844             s/__LOGDIR__/$Conf{LogDir}/g;
845             s/__CONFDIR__/$Conf{ConfDir}/g;
846             s/__TOPDIR__/$Conf{TopDir}/g;
847             s/^(\s*my \$useFHS\s*=\s*)\d;/${1}$opts{fhs};/
848                                     if ( $prog =~ /Lib.pm/ );
849             s/__BACKUPPCUSER__/$Conf{BackupPCUser}/g;
850             s/__CGIDIR__/$Conf{CgiDir}/g;
851             if ( $first && /^#.*bin\/perl/ ) {
852                 #
853                 # Fill in correct path to perl (no taint for >= 2.0.1).
854                 #
855                 print OUT "#!$Conf{PerlPath}\n";
856             } else {
857                 print OUT;
858             }
859             $first = 0;
860         }
861         close(PROG);
862         close(OUT);
863     }
864     die("can't chown $uid, $gid $dest") unless my_chown($uid, $gid, $dest);
865     die("can't chmod $mode $dest")      unless my_chmod($mode, $dest);
866 }
867
868 sub FindProgram
869 {
870     my($path, $prog) = @_;
871
872     if ( defined($opts{"bin-path"}{$prog}) ) {
873         return $opts{"bin-path"}{$prog};
874     }
875     foreach my $dir ( split(/:/, $path) ) {
876         my $file = File::Spec->catfile($dir, $prog);
877         return $file if ( -x $file );
878     }
879     return;
880 }
881
882 sub ConfigParse
883 {
884     my($file) = @_;
885     open(C, $file) || die("can't open $file");
886     binmode(C);
887     my($out, @conf, $var);
888     my $comment = 1;
889     my $allVars = {};
890     my $endLine = undef;
891     while ( <C> ) {
892         if ( /^#/ && !defined($endLine) ) {
893             if ( $comment ) {
894                 $out .= $_;
895             } else {
896                 if ( $out ne "" ) {
897                     $allVars->{$var} = @conf if ( defined($var) );
898                     push(@conf, {
899                         text => $out,
900                         var => $var,
901                     });
902                 }
903                 $var = undef;
904                 $comment = 1;
905                 $out = $_;
906             }
907         } elsif ( /^\s*\$Conf\{([^}]*)/ ) {
908             $comment = 0;
909             if ( defined($var) ) {
910                 $allVars->{$var} = @conf if ( defined($var) );
911                 push(@conf, {
912                     text => $out,
913                     var => $var,
914                 });
915                 $out = $_;
916             } else {
917                 $out .= $_;
918             }
919             $var = $1;
920             $endLine = $1 if ( /^\s*\$Conf\{[^}]*} *= *<<(.*);/ );
921             $endLine = $1 if ( /^\s*\$Conf\{[^}]*} *= *<<'(.*)';/ );
922         } else {
923             $endLine = undef if ( defined($endLine) && /^\Q$endLine[\n\r]*$/ );
924             $out .= $_;
925         }
926     }
927     if ( $out ne "" ) {
928         $allVars->{$var} = @conf if ( defined($var) );
929         push(@conf, {
930             text => $out,
931             var  => $var,
932         });
933     }
934     close(C);
935     return (\@conf, $allVars);
936 }
937
938 sub ConfigMerge
939 {
940     my($old, $oldVars, $new, $newVars) = @_;
941     my $posn = 0;
942     my $res;
943
944     #
945     # Find which config parameters are not needed any longer
946     #
947     foreach my $var ( @$old ) {
948         next if ( !defined($var->{var}) || defined($newVars->{$var->{var}}) );
949         #print(STDERR "Deleting old config parameter $var->{var}\n");
950         $var->{delete} = 1;
951     }
952     #
953     # Find which config parameters are new
954     #
955     foreach my $var ( @$new ) {
956         next if ( !defined($var->{var}) );
957         if ( defined($oldVars->{$var->{var}}) ) {
958             $posn = $oldVars->{$var->{var}};
959         } else {
960             #print(STDERR "New config parameter $var->{var}: $var->{text}\n");
961             push(@{$old->[$posn]{new}}, $var);
962         }
963     }
964     #
965     # Create merged config file
966     #
967     foreach my $var ( @$old ) {
968         next if ( $var->{delete} );
969         push(@$res, $var);
970         foreach my $new ( @{$var->{new}} ) {
971             push(@$res, $new);
972         }
973     }
974     return $res;
975 }
976
977 sub my_chown
978 {
979     my($uid, $gid, $file) = @_;
980
981     return 1 if ( !$opts{"set-perms"} );
982     return chown($uid, $gid, $file);
983 }
984
985 sub my_chmod
986 {
987     my ($mode, $file) = @_;
988
989     return 1 if ( !$opts{"set-perms"} );
990     return chmod($mode, $file);
991 }
992
993 sub prompt
994 {
995     my($question, $default, $option) = @_;
996
997     $default = $opts{$option} if ( defined($opts{$option}) );
998     if ( $opts{batch} ) {
999         print("$question [$default]\n");
1000         return $default;
1001     }
1002     print("$question [$default]? ");
1003     my $reply = <STDIN>;
1004     $reply =~ s/[\n\r]*//g;
1005     return $reply if ( $reply !~ /^$/ );
1006     return $default;
1007 }
1008
1009 __END__
1010
1011 =head1 SYNOPSIS
1012
1013 configure.pl [options]
1014
1015 =head1 DESCRIPTION
1016
1017 configure.pl is a script that is used to install or upgrade a BackupPC
1018 installation.  It is usually run interactively without arguments.  It
1019 also supports a batch mode where all the options can be specified
1020 via the command-line.
1021
1022 For upgrading BackupPC you need to make sure that BackupPC is not
1023 running prior to running BackupPC.
1024
1025 Typically configure.pl needs to run as the super user (root).
1026
1027 =head1 OPTIONS
1028
1029 =over 8
1030
1031 =item B<--batch>
1032
1033 Run configure.pl in batch mode.  configure.pl will run without
1034 prompting the user.  The other command-line options are used
1035 to specify the settings that the user is usually prompted for.
1036
1037 =item B<--backuppc-user=USER>
1038
1039 Specify the BackupPC user name that owns all the BackupPC
1040 files and runs the BackupPC programs.  Default is backuppc.
1041
1042 =item B<--bin-path PROG=PATH>
1043
1044 Specify the path for various external programs that BackupPC
1045 uses.  Several --bin-path options may be specified.  configure.pl
1046 usually finds sensible defaults based on searching the PATH.
1047 The format is:
1048
1049     --bin-path PROG=PATH
1050
1051 where PROG is one of perl, tar, smbclient, nmblookup, rsync, ping,
1052 df, ssh, sendmail, hostname, split, par2, cat, gzip, bzip2 and
1053 PATH is that full path to that program.
1054
1055 Examples
1056
1057     --bin-path cat=/bin/cat --bin-path bzip2=/home/user/bzip2
1058
1059 =item B<--compress-level=N>
1060
1061 Set the configuration compression level to N.  Default is 3
1062 if Compress::Zlib is installed.
1063
1064 =item B<--config-path CONFIG_PATH>
1065
1066 Path to the existing config.pl configuration file for BackupPC.
1067 This option should be specified for batch upgrades to an
1068 existing installation.  The option should be omitted when
1069 doing a batch new install.
1070
1071 =item B<--cgi-dir CGI_DIR>
1072
1073 Path to Apache's cgi-bin directory where the BackupPC_Admin
1074 script will be installed.  This option only needs to be
1075 specified for a batch new install.
1076
1077 =item B<--data-dir DATA_DIR>
1078
1079 Path to the BackupPC data directory.  This is where all the backup
1080 data is stored, and it should be on a large file system. This option
1081 only needs to be specified for a batch new install.
1082
1083 Example:
1084
1085     --data-dir /data/BackupPC
1086
1087 =item B<--dest-dir DEST_DIR>
1088
1089 An optional prefix to apply to all installation directories.
1090 Usually this is not needed, but certain auto-installers like
1091 to stage an install in a temporary directory, and then copy
1092 the files to their real destination.  This option can be used
1093 to specify the temporary directory prefix.  Note that if you
1094 specify this option, BackupPC won't run correctly if you try
1095 to run it from below the --dest-dir directory, since all the
1096 paths are set assuming BackupPC is installed in the intended
1097 final locations.
1098
1099 =item B<--fhs>
1100
1101 Use locations specified by the Filesystem Hierarchy Standard
1102 for installing BackupPC.  This is enabled by default for new
1103 installatios.  To use the pre-3.0 installation locations,
1104 specify --no-fhs.
1105
1106 =item B<--help|?>
1107
1108 Print a brief help message and exits.
1109
1110 =item B<--hostname HOSTNAME>
1111
1112 Host name (this machine's name) on which BackupPC is being installed.
1113 This option only needs to be specified for a batch new install.
1114
1115 =item B<--html-dir HTML_DIR>
1116
1117 Path to an Apache html directory where various BackupPC image files
1118 and the CSS files will be installed.  This is typically a directory
1119 below Apache's DocumentRoot directory.  This option only needs to be
1120 specified for a batch new install.
1121
1122 Example:
1123
1124     --html-dir /usr/local/apache/htdocs/BackupPC
1125
1126 =item B<--html-dir-url URL>
1127
1128 The URL (without http://hostname) required to access the BackupPC html
1129 directory specified with the --html-dir option.  This option only needs
1130 to be specified for a batch new install.
1131
1132 Example:
1133
1134     --html-dir-url /BackupPC
1135
1136 =item B<--install-dir INSTALL_DIR>
1137
1138 Installation directory for BackupPC scripts, libraries, and
1139 documentation.  This option only needs to be specified for a
1140 batch new install.
1141
1142 Example:
1143
1144     --install-dir /usr/local/BackupPC
1145
1146 =item B<--man>
1147
1148 Prints the manual page and exits.
1149
1150 =item B<--set-perms>
1151
1152 When installing files and creating directories, chown them to
1153 the BackupPC user and chmod them too.  This is enabled by default.
1154 To disable (for example, if staging a destination directory)
1155 then specify --no-set-perms.
1156
1157 =item B<--uid-ignore>
1158
1159 configure.pl verifies that the script is being run as the super user
1160 (root).  Without the --uid-ignore option, in batch mode the script will
1161 exit with an error if not run as the super user, and in interactive mode
1162 the user will be prompted.  Specifying this option will cause the script
1163 to continue even if the user id is not root.
1164
1165 =head1 EXAMPLES
1166
1167 For a standard interactive install, run without arguments:
1168
1169     configure.pl
1170
1171 For a batch new install you need to specify answers to all the
1172 questions that are normally prompted:
1173
1174     configure.pl                                   \
1175         --batch                                    \
1176         --cgi-dir /var/www/cgi-bin/BackupPC        \
1177         --data-dir /data/BackupPC                  \
1178         --hostname myHost                          \
1179         --html-dir /var/www/html/BackupPC          \
1180         --html-dir-url /BackupPC                   \
1181         --install-dir /usr/local/BackupPC
1182
1183 For a batch upgrade, you only need to specify the path to the
1184 configuration file:
1185         
1186     configure.pl --batch --config-path /data/BackupPC/conf/config.pl
1187
1188 =head1 AUTHOR
1189
1190 Craig Barratt <cbarratt@users.sourceforge.net>
1191
1192 =head1 COPYRIGHT
1193
1194 Copyright (C) 2001-2004  Craig Barratt.
1195
1196 This program is free software; you can redistribute it and/or modify
1197 it under the terms of the GNU General Public License as published by
1198 the Free Software Foundation; either version 2 of the License, or
1199 (at your option) any later version.
1200
1201 =cut