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