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