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