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