]> git.sur5r.net Git - bacula/bacula/blob - gui/bimagemgr/bimagemgr.pl
5384c681c94e7c68cb9e7b399f196e32b6466ff8
[bacula/bacula] / gui / bimagemgr / bimagemgr.pl
1 #!/usr/bin/perl
2 ##
3 # bimagemgr.pl
4 # burn manager for bacula CD image files
5 #
6 # Copyright (C) 2004-2006 Kern Sibbald
7 #
8 # Thu Dec 09 2004 D. Scott Barninger <barninger at fairfieldcomputers.com>
9 # ASSIGNMENT OF COPYRIGHT
10 # FOR VALUE RECEIVED, D. Scott Barninger hereby sells, transfers and 
11 # assigns unto Kern Sibbald, his successors, assigns and personal representatives, 
12 # all right, title and interest in and to the copyright in this software.
13 # D. Scott Barninger warrants good title to said copyright, that it is 
14 # free of all liens, encumbrances or any known claims against said copyright.
15 #
16 # This program is free software; you can redistribute it and/or
17 # modify it under the terms of the GNU General Public
18 # License version 2 as published by the Free Software Foundation.
19 #
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23 # General Public License for more details.
24 #
25 # You should have received a copy of the GNU General Public
26 # License along with this program; if not, write to the Free
27 # Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
28 # MA 02111-1307, USA.
29 ##
30
31 my $VERSION = "0.3";
32
33 require 5.000; use strict 'vars', 'refs', 'subs';
34 use DBI;
35
36 # get program configuration
37 use config;
38
39 my $prog_config = config->new();
40
41 ## web server configuration
42 #
43 # web server path to program from server root
44 my $prog_name = $prog_config->{'prog_name'};
45 #
46 # web server host
47 my $http_host = $prog_config->{'http_host'};
48 #
49 # path to graphics from document root
50 my $logo_graphic = $prog_config->{'logo_graphic'};
51 my $spacer_graphic = $prog_config->{'spacer_graphic'};
52 my $burn_graphic = $prog_config->{'burn_graphic'};
53 ##
54
55 ## database configuration
56 #
57 # database name
58 my $database = $prog_config->{'database'} ;
59 #
60 # database host
61 my $host = $prog_config->{'host'};
62 #
63 # database user
64 my $user = $prog_config->{'user'};
65 #
66 # database password
67 my $password = $prog_config->{'password'};
68 #
69 # database driver selection
70 my $db_driver = $prog_config->{'db_driver'};
71 my $db_name_param = $prog_config->{'db_name_param'};
72 my $catalog_dump = $prog_config->{'catalog_dump'};
73 my $sqlitebindir = $prog_config->{'sqlitebindir'};
74 my $bacula_working_dir = $prog_config->{'bacula_working_dir'};
75
76 # path to backup files
77 my $image_path = $prog_config->{'image_path'};
78
79 ## path to cdrecord and burner settings
80 my $cdrecord = $prog_config->{'cdrecord'};
81 my $mkisofs = $prog_config->{'mkisofs'};
82 my $cdburner = $prog_config->{'cdburner'};
83 my $burner_speed = $prog_config->{'burner_speed'};
84 my $burnfree = $prog_config->{'burnfree'};
85 ##
86
87 # temporary files
88 my $tempfile=$prog_config->{'tempfile'};
89 my $tempfile_path=$prog_config->{'tempfile_path'};
90 my $working_dir=$prog_config->{'working_dir'};
91
92 # copyright info for page footer
93 my $copyright = "Copyright &copy; 2004-2006 The Bacula Team";
94
95 my %input = &getcgivars;
96 my $action = $input{'action'};
97 my $vol = $input{'vol'};
98
99 &Main();
100 exit;
101
102 #-------------------------------------------------------------------#
103 # Function Main
104 # Description: check requested action and call appropriate subroutine
105 #
106
107 sub Main {
108         # set default action & department
109         if (!$action) {$action = "display"};
110
111         if ($action eq "display") {
112                 &Display();
113         }
114         elsif ($action eq "burn") {
115                 &Burn();
116         }
117         elsif ($action eq "reset") {
118                 &Reset();
119         }
120         elsif ($action eq "version") {
121                 &DisplayHeader();
122                 print "<div align=\"center\">";
123                 print "<br>Bacula CD Image Manager version $VERSION<br><br>";
124                 print "Copyright &copy; 2004 D. Scott Barninger<br>";
125                 print "Licensed under the GNU GPL version 2.0";
126                 print "</div>";
127                 print "</body></html>";
128         }
129         else {
130                 &HTMLdie("Unknown action $action","So sorry Kimosabe..");
131         }
132         return;
133 }
134
135 #-------------------------------------------------------------------#
136 # Function Display
137 # Description: main page display routine
138
139 sub Display {
140         my ($MediaId,$VolumeName,$LastWritten,$VolWrites,$VolStatus,$data);
141
142         &DisplayHeader();
143         &UpdateImageTable();
144
145         # connect to database
146         my ($dbh);
147         if ( $db_driver eq "SQLite" ) {
148                 $dbh = DBI->connect("DBI:$db_driver:$db_name_param=$bacula_working_dir/$database.db","","",
149                         {'RaiseError' => 1}) || &HTMLdie("Unable to connect to database.");
150         }
151         else {
152                 $dbh = DBI->connect("DBI:$db_driver:$db_name_param=$database;host=$host","$user","$password",
153                         {'RaiseError' => 1}) || &HTMLdie("Unable to connect to database.");
154         }
155         my $sth = $dbh->prepare("SELECT Media.VolumeName,Media.LastWritten,CDImages.LastBurn,
156                 Media.VolWrites,Media.VolStatus 
157                 FROM CDImages,Media 
158                 WHERE CDImages.MediaId = Media.MediaId");
159         
160         print "<div align=\"center\">";
161         print "<table width=\"80%\" border=\"0\">";
162         print "<tr>";
163         print "<td colspan=\"6\"><b>";
164         print "<p>Backup files which need to be committed to CDR disk since their last write date are shown below with a Burn button. ";
165         print "Place a blank CDR disk in the drive and click the Burn button for the volume you wish to burn to disk.</p>";
166         print "<p>When CD recording is complete the popup window will display the output of cdrecord. ";
167         print "A successful burn is indicated by the last line showing that all bytes were successfully recorded.</p>";
168         print "<p>After the popup window indicates that the burn is complete, close the popup and <a href=\"$prog_name\">refresh this window</a>. ";
169         print "If the burn is not successful click the Reset link under the last burn date to restore the Burn button and <a href=\"$prog_name\">refresh this window</a>.</p>";
170         print "<p>To burn a copy of your catalog click the catalog Burn button at the bottom of the page. ";
171         print "Up to date copies of your catalog and all backup volumes ensure that your bacula server can be rebuilt in the event of a catastrophe.</p>";
172         print "</b></td>";
173         print "</tr>";
174         print "<tr>";
175         print "<td colspan=\"6\">&nbsp;</td>";
176         print "</tr>";
177         print "</table>";
178         print "<table width=\"80%\" border=\"1\">";
179         print "<tr>";
180         print "<td align=\"center\" colspan=\"6\"><h3>Current Volume Information</h3>Make sure the backup file path $image_path is mounted.</td>";
181         print "</tr>";
182         print "<tr>";
183         print "<td align=\"center\"><b>Volume Name</b></td>";
184         print "<td align=\"center\"><b>Last Written</b></td>";
185         print "<td align=\"center\"><b>Last Burn</b></td>";
186         print "<td align=\"center\"><b>Writes</b></td>";
187         print "<td align=\"center\"><b>Status</b></td>";
188         print "<td align=\"center\">&nbsp;</td>";
189         print "</tr>";
190
191         $sth->execute();
192         while ($data = $sth->fetchrow_arrayref) {
193                 print "<tr>";
194                 print "<td align=\"center\">$$data[0]</td>";
195                 print "<td align=\"center\">$$data[1]</td>";
196                 print "<td align=\"center\">$$data[2]<br><a href=\"$prog_name?action=reset&vol=$$data[0]\">Reset</a></td>";
197                 print "<td align=\"center\">$$data[3]</td>";
198                 print "<td align=\"center\">$$data[4]</td>";
199                 if ($$data[1] gt $$data[2] && $$data[3] gt "0" && $$data[4] ne "Purged") {
200                         print "<td align=\"center\"><form><input type=button value=\"Burn\" onClick=\"BurnWindow=window.open(\'$prog_name?action=burn&vol=$$data[0]\', \'BurnWindow\', \'scrollbars=yes,menubar=no,width=550,height=450,screenX=0,screenY=15\')\"></form></td>";
201                 }
202                 else {
203                         print "<td align=\"center\">No Burn</td>";
204                 }
205                 print "</tr>";
206         }
207         $sth->finish();
208         $dbh->disconnect();
209         
210         print "<tr>";
211         print "<td align=\"center\" colspan=\"6\"><img src=\"$spacer_graphic\" height=\"18\"><form><input type=button value=\"Burn Catalog\" onClick=\"BurnWindow=window.open(\'$prog_name?action=burn&vol=bacula.sql\', \'BurnWindow\', \'scrollbars=yes,menubar=no,width=550,height=450,screenX=0,screenY=15\')\"><img src=\"$spacer_graphic\" width=\"5\"><input type=button value=\"Blank CDRW\" onClick=\"BurnWindow=window.open(\'$prog_name?action=burn&vol=blank\', \'BurnWindow\', \'scrollbars=yes,menubar=no,width=550,height=450,screenX=0,screenY=15\')\"></form><img src=\"$spacer_graphic\" height=\"1\"></td>";
212         print "</tr>";
213         print "</table>";
214         print "</div>";
215         print "<p><p>";
216         &DisplayFooter();
217         return;
218 }
219
220 #-------------------------------------------------------------------#
221 # Function Burn
222 # Description: burn cd images
223
224 sub Burn {
225
226         my $Volume = "$image_path/$vol";
227         
228         # check to see if this is a catalog request
229         if ($vol eq "bacula.sql") {
230                 $Volume = "$working_dir/bacula.sql";
231         }
232         # check to see if this is a blanking request
233         if ($vol eq "blank") {
234                 $Volume = "Blank CD/RW";
235         }
236         
237         # open the burn results file and write header info
238         open(OUTF,">$tempfile_path") || &HTMLdie("Unable to open temporary output file.");
239         print OUTF "<html>";
240         print OUTF "<head>";
241         print OUTF "<title>Burning Volume $Volume</title>";
242         print OUTF "</head>";
243         print OUTF "<body>";
244         print OUTF "<div align=\"center\">";
245         print OUTF "<table width=\"100%\">";
246         print OUTF "<tbody>";
247         print OUTF "<tr>";
248         print OUTF "<td align=\"center\">  <img src=\"$logo_graphic\"> </td>";
249         print OUTF "</tr>";
250         print OUTF "</tbody>";
251         print OUTF "</table>";
252         print OUTF "</div>";
253         close(OUTF);
254         
255         # now send to the burn status window that we are burning
256         print "Content-type: text/html\n\n";
257         print "<html>";
258         print "<head>";
259         print "<meta http-equiv=\"Refresh\" content=\"3; url=http://$http_host/$tempfile\">";
260         print "<title>Burning Volume $Volume</title>";
261         print "</head>";
262         print "<body>";
263         print "<div align=\"center\">";
264         print "<table width=\"100%\">";
265         print "<tbody>";
266         print "<tr>";
267         print "<td align=\"center\">  <img src=\"$logo_graphic\"> </td>";
268         print "</tr>";
269         print "<tr>";
270         print "<td align=\"center\">  <img src=\"$spacer_graphic\" height=\"10\"> </td>";
271         print "</tr>";
272         print "<tr>";
273         print "<td align=\"center\">  <img src=\"$burn_graphic\"> </td>";
274         print "</tr>";
275         print "</tbody>";
276         print "</table>";
277         print "<p>Now burning $Volume ...</p>";
278         print "</div>";
279         print "</body>";
280         print "</html>";
281         
282         # check to see if this is a catalog request
283         if ($vol eq "bacula.sql") {
284                 system("$catalog_dump > $working_dir/bacula.sql");
285         }
286         
287         # check to see if this is a blanking request
288         if ($vol eq "blank") {
289                 system("$cdrecord -eject speed=2 dev=$cdburner blank=fast >> $tempfile_path");
290         }
291         else {
292         # burn the image and clean up
293         system("$mkisofs -o $working_dir/temp.iso -J -r -V $vol $Volume");
294         system("$cdrecord -eject $burnfree speed=$burner_speed dev=$cdburner $working_dir/temp.iso >> $tempfile_path");
295         system("rm -f $working_dir/temp.iso");
296         }
297         
298         if ($vol eq "bacula.sql") {
299                 system("rm -f $working_dir/bacula.sql");
300         }
301                 
302         # finish up the burn results file
303         open(OUTF,">>$tempfile_path") || &HTMLdie("Unable to open temporary output file.");
304         print OUTF "<table width=\"100%\">";
305         print OUTF "<tbody>";
306         print OUTF "<tr>";
307         print OUTF "<td><div align=\"center\">If you do not see successful output from cdrecord above the burn has failed.</div></td>";
308         print OUTF "</tr>";
309         print OUTF "<tr>";
310         print OUTF "<td><div align=\"center\">Please close this window and refresh the main window.</div></td>";
311         print OUTF "</tr>";
312         print OUTF "<tr>";
313         print OUTF "<td><div align=\"center\"><font size=\"-3\"> $copyright </font></div></td>";
314         print OUTF "</tr>";
315         print OUTF "</tbody>";
316         print OUTF "</table>";
317         print OUTF "</div>";
318         print OUTF "</body>";
319         print OUTF "</html>";
320         close(OUTF);
321         
322         # now pretty up the burn results file by replacing \n with <br>
323         open(INFILE, "$tempfile_path") || &HTMLdie("Unable to open input file $tempfile_path");
324         open(OUTFILE, ">$working_dir/bimagemgr-temp") || &HTMLdie("Unable to open output file bimagemgr-temp");
325         while(my $line = <INFILE>) {
326                 $line =~ s/\n/<br>/g;
327                 print OUTFILE ($line);
328         }
329         close(INFILE);
330         close(OUTFILE);
331         system("cp -f $working_dir/bimagemgr-temp $tempfile_path");
332         
333         if ($vol ne "bacula.sql" && $vol ne "blank") {
334                 ## update the burn date in the CDImages table
335                 # get current timestamp
336                 my($sysdate,$systime,$sysyear,$sysmon,$sysmday) = &SysDate;
337                 my $burndate = "$sysdate $systime";
338         
339                 # connect to database
340                 my ($dbh);
341                 if ( $db_driver eq "SQLite" ) {
342                         $dbh = DBI->connect("DBI:$db_driver:$db_name_param=$bacula_working_dir/$database.db","","",{'RaiseError' => 1}) || &HTMLdie("Unable to connect to database.");
343                 }
344                 else {
345                         $dbh = DBI->connect("DBI:$db_driver:$db_name_param=$database;host=$host","$user","$password",{'RaiseError' => 1}) || &HTMLdie("Unable to connect to database.");
346                 }
347                 # get the MediaId for our volume
348                 my $sth = $dbh->prepare("SELECT MediaId from Media WHERE VolumeName = \"$vol\"");
349                 $sth->execute();
350                 my $media_id = $sth->fetchrow_array;
351                 $sth->finish();
352                 # set LastBurn date
353                 $dbh->do("UPDATE CDImages SET LastBurn = \"$burndate\" WHERE MediaId = $media_id");
354                 $dbh->disconnect();
355                 ##
356         }
357         
358         return;
359 }
360
361 #-------------------------------------------------------------------#
362 # Function UpdateImageTable
363 # Description: update the CDImages table from the Media table
364
365 sub UpdateImageTable {
366
367         my ($data,@MediaId,$id,$exists,$sth1,$sth2);
368
369         # connect to database
370         my ($dbh);
371         if ( $db_driver eq "SQLite" ) {
372                 $dbh = DBI->connect("DBI:$db_driver:$db_name_param=$bacula_working_dir/$database.db","","",
373                         {'RaiseError' => 1}) || &HTMLdie("Unable to connect to database.");
374         }
375         else {
376                 $dbh = DBI->connect("DBI:$db_driver:$db_name_param=$database;host=$host","$user","$password",
377                         {'RaiseError' => 1}) || &HTMLdie("Unable to connect to database.");
378         }
379
380         # get the list of current MediaId
381         $sth1 = $dbh->prepare("SELECT MediaId from Media");
382         $sth1->execute();
383         while ($data = $sth1->fetchrow_arrayref) {
384                 push(@MediaId,$$data[0]);
385         }
386         $sth1->finish();
387
388         # now check if we have a matching row in CDImages
389         # if not then insert a record
390         foreach $id (@MediaId) {
391                 $sth2 = $dbh->prepare("SELECT MediaId from CDImages 
392                 WHERE MediaId = $id");
393                 $sth2->execute();
394                 $exists = $sth2->fetchrow_array;
395                 if ($exists ne $id) {
396                         $dbh->do("INSERT into CDImages VALUES ($id,\"0000-00-00 00:00:00\")");
397                 }
398                 $sth2->finish();
399         }
400
401         # disconnect
402         $dbh->disconnect();
403         return;
404 }
405
406 #-------------------------------------------------------------------#
407 # Function Reset
408 # Description: reset the Last Burn date to 0
409
410 sub Reset {
411         my ($id,$sth);
412
413         # connect to database
414         my ($dbh);
415         if ( $db_driver eq "SQLite" ) {
416                 $dbh = DBI->connect("DBI:$db_driver:$db_name_param=$bacula_working_dir/$database.db","","",
417                         {'RaiseError' => 1}) || &HTMLdie("Unable to connect to database.");
418         }
419         else {
420                 $dbh = DBI->connect("DBI:$db_driver:$db_name_param=$database;host=$host","$user","$password",
421                         {'RaiseError' => 1}) || &HTMLdie("Unable to connect to database.");
422         }
423
424         # get the MediaId
425         $sth = $dbh->prepare("SELECT MediaId FROM Media WHERE VolumeName=\"$vol\"");
426         $sth->execute();
427         $id = $sth->fetchrow_array;
428         $sth->finish();
429         
430         # reset the date
431         $dbh->do("UPDATE CDImages SET LastBurn=\"0000-00-00 00:00:00\" 
432                         WHERE MediaId=$id") || &HTMLdie("Unable to update Last Burn Date.");
433
434         $dbh->disconnect();
435         
436         print "Location:http://$http_host$prog_name\n\n";
437 }
438
439 #-------------------------------------------------------------------#
440 # Function DisplayHeader
441 # Description: main page display header
442
443 sub DisplayHeader {
444         my($title)= @_ ;
445         $title || ($title= "Bacula CD Image Manager") ;
446         print "Content-type: text/html\n\n";
447         print "<html>";
448         print "<head>";
449         print "<title>$title</title>";
450         print "</head>";
451         print "<body>";
452         print "<div align=\"center\">";
453         print "<table width=\"100%\">";
454         print "<tbody>";
455         print "<tr>";
456         print "<td align=\"center\"><a href=\"$prog_name?action=version\" alt=\"About bimagemgr\"><img src=\"$logo_graphic\" border=\"0\"></a></td>";
457         print "</tr>";
458         print "</tbody>";
459         print "</table>";
460         print "</div>";
461         print "<p></p>";
462         return;
463 }
464
465 #-------------------------------------------------------------------#
466 # Function DisplayFooter
467 # Description: main page display footer
468
469 sub DisplayFooter {
470         print "<table width=\"100%\">";
471         print "<tbody>";
472         print "<tr>";
473         print "<td><div align=\"center\"><font size=\"-3\"> $copyright </font></div></td>";
474         print "</tr>";
475         print "</tbody>";
476         print "</table>";
477         print "</div>";
478         print "</body>";
479         print "</html>";
480         return;
481 }
482
483 #-------------------------------------------------------------------#
484 # Function SysDate
485 # Description: get current date/time
486 #
487 sub SysDate {
488         # usage:  my($sysdate,$systime,$sysyear,$sysmon,$sysmday) = &SysDate;
489
490         my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
491         if (length ($min) == 1) {$min = '0'.$min;}
492         if (length ($sec) == 1) {$sec = '0'.$sec;}
493         # since localtime returns the month as 0-11
494         $mon = $mon + 1;
495         if (length ($mon) == 1) {$mon = '0'.$mon;}
496         if (length ($mday) == 1) {$mday = '0'.$mday;}
497         # since localtime returns the year as the number of years since 1900
498         # ie year is 100 in the year 2000 (so is y2k OK)
499         $year = $year + 1900;
500         my $date = "$year-$mon-$mday";
501         my $time = "$hour:$min:$sec";
502         return($date,$time,$year,$mon,$mday);
503 }
504
505 #-------------------------------------------------------------------#
506 # Function getcgivars
507 # Read all CGI vars into an associative array.
508 # courtesy James Marshall james@jmarshall.com http://www.jmarshall.com/easy/cgi/
509 # If multiple input fields have the same name, they are concatenated into
510 #   one array element and delimited with the \0 character (which fails if
511 #   the input has any \0 characters, very unlikely but conceivably possible).
512 # Currently only supports Content-Type of application/x-www-form-urlencoded.
513 sub getcgivars {
514         my($in, %in) ;
515         my($name, $value) ;
516
517
518                 # First, read entire string of CGI vars into $in
519         if ( ($ENV{'REQUEST_METHOD'} eq 'GET') ||
520                         ($ENV{'REQUEST_METHOD'} eq 'HEAD') ) {
521                 $in= $ENV{'QUERY_STRING'} ;
522
523         } elsif ($ENV{'REQUEST_METHOD'} eq 'POST') {
524                 if ($ENV{'CONTENT_TYPE'}=~ m#^application/x-www-form-urlencoded$#i) {
525                         length($ENV{'CONTENT_LENGTH'})
526                                 || &HTMLdie("No Content-Length sent with the POST request.") ;
527                         read(STDIN, $in, $ENV{'CONTENT_LENGTH'}) ;
528
529                 } else { 
530                         &HTMLdie("Unsupported Content-Type: $ENV{'CONTENT_TYPE'}") ;
531                 }
532
533         } else {
534                 &HTMLdie("Script was called with unsupported REQUEST_METHOD.") ;
535         }
536
537                 # Resolve and unencode name/value pairs into %in
538         foreach (split('&', $in)) {
539                 s/\+/ /g ;
540                 ($name, $value)= split('=', $_, 2) ;
541                 $name=~ s/%(..)/chr(hex($1))/ge ;
542                 $value=~ s/%(..)/chr(hex($1))/ge ;
543                 $in{$name}.= "\0" if defined($in{$name}) ;  # concatenate multiple vars
544                 $in{$name}.= $value ;
545         }
546
547         return %in ;
548
549 }
550
551 #-------------------------------------------------------------------#
552 # Function HTMLdie
553 # Description: Die, outputting HTML error page
554 # If no $title, use a default title
555 sub HTMLdie {
556         my ($msg,$title)= @_ ;
557         $title || ($title= "CGI Error") ;
558         print "Content-type: text/html\n\n";
559         print "<html>";
560         print "<head>";
561         print "<title>$title</title>";
562         print "</head>";
563         print "<body>";
564         print "<h1>$title</h1>";
565         print "<h3>$msg</h3>";
566         print "<form>";
567         print "<input type=button name=\"BackButton\" value=\"<- Back\" id=\"Button1\" onClick=\"history.back()\">";
568         print "</form>";
569         print "</body>";
570         print "</html>";
571
572         exit ;
573 }
574