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