3 # Copyright (c) 2006 Zmanda Inc. All Rights Reserved.
5 # This program is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License version 2 as published
7 # by the Free Software Foundation.
9 # This program is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 # You should have received a copy of the GNU General Public License along
15 # with this program; if not, write to the Free Software Foundation, Inc.,
16 # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 # Contact information: Zmanda Inc, 505 N Mathlida Ave, Suite 120
19 # Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
25 use Socket; # for gethostbyname
27 my $confdir="@CONFIG_DIR@";
28 my $prefix="@prefix@";
29 my $tmpdir="@AMANDA_DBGDIR@";
30 $prefix=$prefix; # avoid warnings about possible typo
31 my $exec_prefix="@exec_prefix@";
32 $exec_prefix=$exec_prefix; # ditto
33 my $sbindir="@sbindir@";
34 my $localstatedir="@localstatedir@";
35 my $amandahomedir="$localstatedir/lib/amanda";
36 my $datadir="$amandahomedir/template.d"; #rpm install template files here
37 my $def_tapedev="file:/$amandahomedir/vtapes";
39 my $amanda_user="@CLIENT_LOGIN@";
40 my $def_config="@DEFAULT_CONFIG@";
41 my $def_dtimeout="1800";
42 my $def_ctimeout="30";
43 my $def_etimeout="300";
45 my $amanda_conf_perm=0600;
53 # Get the version suffix.
54 my $USE_VERSION_SUFFIXES = '@USE_VERSION_SUFFIXES@';
56 if ( $USE_VERSION_SUFFIXES =~ /^yes$/i ) {
64 print "\t\t <config> [--template <template>]\n";
65 print "\t\t[--no-vtape] (do not create virtual tapes)\n";
66 print "\t\t[--tapetype <tapetype>] [--tpchanger <tpchanger>]\n";
67 print "\t\t[--tapedev <tapedev>] [--changerfile <changerfile>]\n";
68 print "\t\t[--changerdev <changerdev>] [--labelstr <labelstr>] \n";
69 print "\t\t[--mailto <mailto>] [--dumpcycle <dumpcycle> (ex: 5days, 1week or 2weeks)]\n";
70 print "\t\t[--runspercycle <runspercycle>] [--runtapes <runtapes>]\n";
71 print "\t\t[--tapecycle <tapecycle>]\n";
72 print "\t\t[--help]\n";
77 for $fh ( STDOUT, LOG ) {
83 my ($err, $cleanup) = @_;
85 # clean up $config directory if cleanup=1
86 # if error in creating vtape or holding disk,
87 # advise user to create manually, no need to cleanup
88 if ( $cleanup && defined $config && -e "$confdir/$config" ) {
89 print LOG "cleaning up $confdir/$config\n";
90 if ( -e "$confdir/$config/amanda.conf" ) {
91 unlink "$confdir/$config/amanda.conf" ||
92 print LOG "unlink $confdir/$config/amanda.conf failed: $!\n";
94 if ( -e "$confdir/$config/advanced.conf" ) {
95 unlink "$confdir/$config/advanced.conf" ||
96 print LOG "unlink $confdir/$config/advanced.conf failed: $!\n";
98 if ( -e "$confdir/$config/tapelist" ) {
99 unlink "$confdir/$config/tapelist" ||
100 print LOG "unlink $confdir/$config/tapelist failed: $!\n";
102 if ( -e "$confdir/$config/curinfo" ) {
103 rmdir "$confdir/$config/curinfo" ||
104 print LOG "rmdir $confdir/$config failed: $!\n";
106 if ( -e "$confdir/$config/index" ) {
107 rmdir "$confdir/$config/index" ||
108 print LOG "rmdir $confdir/$config/index failed: $!\n";
110 rmdir "$confdir/$config" ||
111 print LOG "rmdir $confdir/$config failed: $!\n";
120 ( $user eq $amanda_user ) ||
121 die ("ERROR: $0 must be run by $amanda_user\n", 0);
125 # rpm installation should have taken care of these. Create one if it's not there
126 sub check_gnutarlist_dir {
127 if ( -e "$amandahomedir/gnutar-lists" ) {
128 &mprint ("$amandahomedir/gnutar-lists directory exists\n");
131 mkdir ("$amandahomedir/gnutar-lists", $def_perm) ||
132 &log_and_die ("ERROR: mkdir:$amandahomedir/gnutar-lists failed: $!\n", 0);
136 sub create_conf_dir {
137 unless ( -e $confdir ) {
138 &log_and_die ("ERROR: $confdir does not exist\n", 0);
140 unless ( -e "$confdir/$config" ) {
141 mkdir ("$confdir/$config", $def_perm) ||
142 &log_and_die ("ERROR: mkdir: $confdir/$config failed: $!\n", 0); # $! = system error
144 &log_and_die ("ERROR: Configuration $config exists\n", 0);
146 unless ( -e "$confdir/template.d" ) {
147 mkdir ("$confdir/template.d", $def_perm) ||
148 &log_and_die ("ERROR: mkdir: $confdir/template.d failed: $!\n", 0);
149 &mprint ("$confdir/template.d directory created\n");
153 sub copy_template_file {
156 &log_and_die ("ERROR: template is missing\n", 1);
158 # create and update amanda.conf
159 open(CONF, "$datadir/amanda-$tplate.conf")
160 || &log_and_die ("ERROR: Cannot open $datadir/amanda-$tplate.conf: $!\n", 1);
161 open(NEWCONF, ">$confdir/$config/amanda.conf") ||
162 &log_and_die ("ERROR: Cannot create $confdir/$config/amanda.conf: $!\n", 1);
163 chmod ($amanda_conf_perm, "$confdir/$config/amanda.conf") ||
164 &log_and_die ("ERROR: Cannot set amanda.conf file access permission: $!\n", 1);
166 $_ =~ s/$def_config/$config/;
171 &mprint ("$confdir/$config/amanda.conf created and updated\n");
175 sub create_curinfo_index_dir {
176 mkdir("$confdir/$config/curinfo", $def_perm) ||
177 &log_and_die ("ERROR: mkdir: $confdir/$config/curinfo failed: $!\n", 1);
178 mkdir("$confdir/$config/index", $def_perm) ||
179 &log_and_die ("ERROR: mkdir: $confdir/$config/index failed: $!\n", 1);
180 &mprint ("curinfo and index directory created\n");
183 sub touch_list_files {
184 open (TLIST, ">$confdir/$config/tapelist")
185 || &log_and_die ("ERROR: Cannot create tapelist file: $!\n", 1);
187 &mprint ("tapelist file created\n");
189 open (DLIST, ">$confdir/$config/disklist")
190 || &log_and_die ("ERROR: Cannot create disklist file: $!\n", 1);
192 &mprint ("disklist file created\n");
195 # create holding disk directory, check disk space first
197 if ( -d "$amandahomedir/holdings/$config" ) {
198 my $uid = (stat("$amandahomedir/holdings/$config"))[4];
199 my $owner = (getpwuid($uid))[0];
200 unless ( $owner eq $amanda_user ) {
201 &mprint ("WARNING: holding disk directory exists and is not owned by $amanda_user\n");
207 my $out = `df -k $amandahomedir`;
208 my @dfout = split(" " , $out);
209 unless ( $#dfout == 12 ) { # df should output 12 elem
210 &mprint ("WARNING: df failed, holding disk directory not created\n");
214 unless (( $dfout[1] eq "1K-blocks" ) || ( $dfout[1] eq "kbytes")) {
215 $div=2; # 512-blocks displayed by df
218 if (( $dfout[10] / $div ) > 1024000 ) { # holding disk is defined 1000 MB
219 &mprint ("creating holding disk directory\n");
220 unless ( -d "$amandahomedir/holdings" ) {
221 mkdir ( "$amandahomedir/holdings", $def_perm) ||
222 (&mprint ("WARNING: mkdir $amandahomedir/holdings failed: $!\n"), $holding_err++, return );
224 mkdir ( "$amandahomedir/holdings/$config", $def_perm) ||
225 (&mprint ("WARNING: mkdir $amandahomedir/holdings/$config failed: $!\n"), $holding_err++, return) ;
229 #create default tape dir
230 sub create_deftapedir{
231 unless ( -e "$amandahomedir/vtapes" ) {
232 mkdir ( "$amandahomedir/vtapes", $def_perm) ||
233 ( &mprint ("WARNING: mkdir $amandahomedir/$config/vtapes failed: $!\n"), return );
235 unless ( -e "$amandahomedir/vtapes/$config" ) {
236 mkdir ( "$amandahomedir/vtapes/$config", $def_perm) ||
237 ( &mprint ("WARNING: mkdir $amandahomedir/vtapes/$config failed: $!\n"), return );
239 $parentdir="$amandahomedir/vtapes/$config";
242 # create and label vtape
244 &mprint ("creating vtape directory\n");
245 if ($template_only==0){ # check $template mode
246 $mylabelprefix=$labelstr; #set labelstr
247 if ($tapedev eq "$def_tapedev/$config"){
251 $tapedev=~/^(file:\/)/;
256 $mylabelprefix=$config;
259 unless ( -e $parentdir){
260 &mprint ("WARNING: tapedev $parentdir does not exists, vtapes creation failed!\n");
261 &mprint ("Please create $parentdir and $confdir/$config and rerun the same command or else create vtapes manually.\n");
266 chdir ("$parentdir") ||
267 ( &mprint("WARNING: chdir $parentdir failed: $!\n"), $vtape_err++, return );
269 &mprint ("amlabel vtapes\n");
270 if (defined $tapecycle) {
274 my $dfout =`df $parentdir`;
276 @dfdata=split(" ",$dfout);
277 unless ( $dfdata[1] eq "1K-blocks" ) {
278 $mul=512; # 512-blocks displayed by df
280 if (($dfdata[10]*$mul) < (($tp_cyclelimit*73728)+10240)){
281 &mprint ("WARNING: Not enough space for vtapes. Creation of vtapes failed\n");
287 $tp_cyclelimit=$def_tapecycle;
290 for $i (1..$tp_cyclelimit) {
291 unless ( -e "slot$i"){
292 mkdir ("slot$i", $def_perm) ||
293 ( &mprint ("WARNING: mkdir $parentdir/slot$i failed: $!\n"), $vtape_err++, return);
295 ( @amlabel_out = `$sbindir/amlabel -f $config $mylabelprefix-$i slot $i`) ||
296 ( &mprint ("WARNING: amlabel vtapes failed at slot $i: $!\n"), $vtape_err++, return);
298 foreach (@amlabel_out) {
301 # reset tape to the first slot
302 `$sbindir/amtape $config reset`;
305 sub create_customconf{
306 # now create a custom amanda.conf from user input
308 { $mailto="$amanda_user"; }
309 else { # untaint mailto which can be evil
310 # reject mailto with the following * ( ) < > [ ] , ; : ! $ \ / "
311 if ( $mailto =~ /^([^\*\(\)<>\[\]\,\;\:\!\$\\\/\"]+)$/ ) {
312 $mailto = $1; # now untainted
314 &log_and_die ("ERROR: Invalid data in mailto.\n"); # log this somewhere
317 unless ( $dumpcycle ) { $dumpcycle="1 week"; }
318 unless ( $runspercycle ) { $runspercycle="5"; }
319 unless ( $tapecycle ) { $tapecycle="10 tapes"; }
320 unless ( $runtapes ) { $runtapes="1"; }
321 unless ( $labelstr ) {
322 if ($template eq "harddisk") {
325 $labelstr="^$config-[0-9][0-9]*\$";
328 if ((!(defined($template)))||($template eq "harddisk"))
330 if (defined $tapedev){
331 $tapedev="file:/".$tapedev;
333 unless ( $tpchanger ) { $tpchanger="chg-disk"; }
334 unless ( $tapedev ) { $tapedev="$def_tapedev/$config"; }
335 unless ( $changerfile ) { $changerfile="$confdir/$config/changer.conf"; }
336 unless ( $changerdev ) { $changerdev="/dev/null";}
337 unless ( $tapetype ) { $tapetype="HARDDISK"; }
339 elsif ($template eq "single-tape")
341 unless ($tpchanger) {$tpchanger="chg-manual";}
342 unless ($tapedev) {$tapedev="/dev/nst0";}
343 unless ($changerfile) {$changerfile="$confdir/$config/chg-manual.conf";}
344 unless ($changerdev) {$changerdev="/dev/null";}
345 unless ($tapetype) {$tapetype="HP-DAT";}
347 elsif ($template eq "tape-changer")
349 unless ($tpchanger){$tpchanger="chg-zd-mtx";}
350 unless ($tapedev){ $tapedev="/dev/nst0";}
351 unless ($changerfile){$changerfile="$confdir/$config/changer.conf";}
352 unless ($changerdev) {$changerdev="/dev/sg1";}
353 unless ($tapetype) {$tapetype="HP-DAT";}
357 unless ($tpchanger){$tpchanger="chg-multi";}
358 unless ($changerfile){$changerfile="$confdir/$config/changer.conf";}
359 unless ($tapetype) {$tapetype="HP-DAT";}
363 open (CONF, ">$confdir/$config/amanda.conf") ||
364 &log_and_die ("ERROR: Cannot create amanda.conf file: $!\n", 1);
365 chmod ($amanda_conf_perm, "$confdir/$config/amanda.conf") ||
366 &log_and_die ("ERROR: Cannot set amanda.conf file access permission: $!\n", 1);
368 print CONF "org \"$config\"\t\t# your organization name for reports\n";
369 print CONF "mailto \"$mailto\"\t# space separated list of operators at your site\n";
370 print CONF "dumpcycle $dumpcycle\t\t# the number of days in the normal dump cycle\n";
371 print CONF "runspercycle $runspercycle\t\t# the number of amdump runs in dumpcycle days\n";
372 print CONF "tapecycle $tapecycle\t# the number of tapes in rotation\n";
373 print CONF "runtapes $runtapes\t\t# number of tapes to be used in a single run of amdump\n";
374 print CONF "tpchanger \"$tpchanger\"\t# the tape-changer glue script\n";
375 print CONF "tapedev \"$tapedev\"\t# the no-rewind tape device\n";
376 print CONF "changerfile \"$changerfile\"\t# tape changer configuration parameter file\n";
377 print CONF "changerdev \"$changerdev\"\t# tape changer configuration parameter device\n";
378 print CONF "tapetype $tapetype\t# what kind of tape it is\n";
379 print CONF "labelstr \"$labelstr\"\t# label constraint regex: all tapes must match\n";
380 print CONF "dtimeout $def_dtimeout\t# number of idle seconds before a dump is aborted\n";
381 print CONF "ctimeout $def_ctimeout\t# max number of secconds amcheck waits for each client\n";
382 print CONF "etimeout $def_etimeout\t# number of seconds per filesystem for estimates\n";
383 print CONF "define dumptype global {\n";
384 print CONF " comment \"Global definitions\"\n";
385 print CONF " auth \"bsdtcp\"\n}\n";
386 print CONF "define dumptype gui-base {\n";
387 print CONF " global\n";
388 print CONF " program \"GNUTAR\"\n";
389 print CONF " comment \"gui base dumptype dumped with tar\"\n";
390 print CONF " compress none\n";
391 print CONF " index yes\n}\n";
392 if ($tapetype eq "HARDDISK") {
393 print CONF "define tapetype HARDDISK {\n";
394 print CONF " comment \"Virtual Tapes\"\n";
395 print CONF " length 5000 mbytes\n}\n";
397 print CONF "includefile \"advanced.conf\"\n";
398 print CONF "includefile \"$confdir/template.d/dumptypes\"\n";
399 print CONF "includefile \"$confdir/template.d/tapetypes\"\n";
401 mprint ("custom amanda.conf created\n");
406 &mprint ("/var/lib/amanda/example/xinetd.amandaserver contains the latest Amanda server daemon configuration.\n");
407 &mprint ("Please merge it to /etc/xinetd.d/amandaserver.\n");
411 sub build_amanda_ssh_key{
412 if ( -e "$amandahomedir/.ssh/id_rsa_amdump.pub" ) {
413 if ( -e "$amandahomedir/.ssh/client_authorized_key" ) {
414 &mprint ("$amandahomedir/.ssh/client_authorized_keys exists.\n");
417 open(NEWAUTH, ">$amandahomedir/.ssh/client_authorized_keys") ||
418 (&mprint("WARNING: open $amandahomedir/.ssh/client_authorized_key failed: $!\n"), return);
419 open(PUB, "$amandahomedir/.ssh/id_rsa_amdump.pub") ||
420 (&mprint("WARNING: open $amandahomedir/.ssh/id_rsa_amdump.pub failed: $!\n"), return);
421 print NEWAUTH "from=\"$host\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,command=\"/usr/lib/amanda/amandad -auth=ssh amdump\" ";
427 &mprint("$amandahomedir/.ssh/client_authorized_keys created. Please append to /var/lib/amanda/.ssh/authorized_keys file on Amanda clients\n");
432 sub copy_chg_manual_conf {
433 if ( $template eq "single-tape" && !defined $changerfile && !defined $tpchanger)
435 my $my_changerfile="$confdir/$config/chg-manual.conf";
436 copy("$datadir/chg-manual.conf", $my_changerfile) ||
437 &mprint ("copy $datadir/chg-manual.conf to $my_changerfile failed: $!\n");
444 $ret = GetOptions ("template=s"=>\$template,
445 "no-vtape!"=>\$novtape,
446 "tapetype=s"=>\$tapetype,
447 "tpchanger=s"=>\$tpchanger,
448 "tapedev=s"=>\$tapedev,
449 "changerfile=s"=>\$changerfile,
450 "changerdev=s"=>\$changerdev,
451 "labelstr=s"=>\$labelstr,
452 "mailto=s"=>\$mailto,
453 "dumpcycle=s"=>\$dumpcycle,
454 "runspercycle=i"=>\$runspercycle,
455 "runtapes=i"=>\$runtapes,
456 "tapecycle=i"=>\$tapecycle,
470 unless ( $#ARGV == 0 ) {
471 print STDERR "ERROR: config name is required.\n";
476 if ( "$ARGV[0]" =~ /^([-\@\w.]+)$/ ) {
477 $config = $1; # now untainted
479 die ("ERROR: Invalid data in config name.\n"); # log this somewhere
484 $oldPATH = $ENV{'PATH'};
486 $ENV{'PATH'} = "/usr/bin:/usr/sbin:/sbin:/bin:/usr/ucb"; # force known path
487 delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
488 $date=`date +%Y%m%d%H%M%S`;
490 my $logfile="$tmpdir/amserverconfig.$date.debug";
493 unless ( -e "$tmpdir" ) {
494 mkdir ("$tmpdir", $def_perm) ||
495 die ("ERROR: mkdir: $tmpdir failed: $!\n");
498 open (LOG, ">$logfile") || die ("ERROR: Cannot create logfile: $!\n");
499 print STDOUT "Logging to $logfile\n";
501 my $lhost=`hostname`;
503 # get our own canonical name, if possible (we don't sweat the IPv6 stuff here)
504 $host=(gethostbyname($lhost))[0];
507 $host = $lhost; #gethostbyname() failed, go with hostname output
513 if ( defined $template ) {
514 # validate user input to template
517 @valid_templates = ( "harddisk", "single-tape", "tape-changer", "s3");
518 foreach $elt (@valid_templates) {
519 if ($elt eq lc($template)) {
525 print STDERR "valid inputs to --templates are harddisk, single-tape, tape-changer or S3\n";
529 # if tape-changer is chosen, check if mtx is installed
530 if ($template eq "tape-changer") {
532 for $dir ("/usr/sbin", "/usr/local/sbin", "/usr/local/bin", "/usr/bin", "/bin","/opt/csw/sbin",split(":",$oldPATH)) {
533 if ( -e "$dir/mtx" ) {
539 &mprint ("ERROR: mtx binary not found, tape-changer template will not work and is not installed.\n");
540 &log_and_die ("ERROR: Please install mtx and rerun the same command.\n", 0);
542 unless ($changerfile) {$changerfile="$confdir/$config/changer.conf";}
543 open (CCONF, ">$changerfile")
544 || &log_and_die ("ERROR: Cannot create $changerfile: $!\n", 1);
546 } elsif ($template eq "S3" ) {
548 unless ($changerfile) {$changerfile="$confdir/$config/changer.conf";}
549 open (CCONF, ">$changerfile")
550 || &log_and_die ("ERROR: Cannot create $changerfile: $!\n", 1);
557 &check_gnutarlist_dir;
559 # copy dumptypes and tapetypes files if none exists.
560 my $dtype="$confdir/template.d/dumptypes";
561 my $ttype="$confdir/template.d/tapetypes";
563 unless ( -e $dtype ) {
564 copy("$datadir/dumptypes", $dtype ) ||
565 &log_and_die ("ERROR: copy dumptypes failed: $!\n", 1);
569 unless ( -e $ttype ) {
570 copy("$datadir/tapetypes", $ttype ) ||
571 &log_and_die ("ERROR: copy tapetypes file to $ttype failed: $!\n", 1);
576 # update $def_config value to the specified config value in advanced.conf
577 open(ADV, "$datadir/advanced.conf") || &log_and_die ("ERROR: Cannot open advanced.conf file: $!\n", 1);
578 open(NEWADV, ">$confdir/$config/advanced.conf") ||
579 &log_and_die ("ERROR: Cannot create advanced.conf file: $!\n", 1);
581 $_ =~ s/$def_config/$config/;
586 &mprint ("$confdir/$config/advanced.conf created and updated\n");
589 &create_curinfo_index_dir;
593 if ( defined $template ) {
594 # if any other parameters are provided, create a workable custom config
595 if ( defined $tapetype || defined $tpchanger || defined $tapedev
596 || defined $changerdev || defined $labelstr || defined $mailto || defined $dumpcycle
597 || defined $runspercycle || defined $runtapes || defined $tapecycle ) {
598 &mprint("Creating custom configuration using templates\n");
600 if ( $template ne "harddisk" ) {
603 if (defined $labelstr) {
604 if ($labelstr=~/^([-\w.]+)$/) {
605 &create_vtape unless ( defined $novtape );
607 &mprint ("WARNING: Only alphanumeric string is supported in labelstr when using template to create vtapes. ");
608 &mprint ("If you want to use regex in labelstr, please create vtapes manually.\n");
614 $tapedev="$def_tapedev/$config";
615 ©_template_file($template);
616 if ($template ne "harddisk") {
617 unless ( -e "$amandahomedir/holdings/$config" ) {
620 } else { # harddisk and template only
621 unless ( -e "$amandahomedir/vtapes/$config" || defined $novtape ) {
626 ©_chg_manual_conf;
632 &build_amanda_ssh_key;
635 &mprint("Error in creating virtual tape, please check log and create virtual tape manually.\n");
639 if ( $holding_err ) {
640 &mprint("Error in creating holding disk, please check log and create holding disk manually.\n");
646 if ( $vtape_err==0 && $holding_err==0) {
652 $ENV{'PATH'} = $oldPATH;