abc23ea0856526a693c5188595423a2b1303a5dc
[php-bacula-disk-changer.git] / bin / pbdc.php
1 <?php
2
3 $BASE_LIB=$_SERVER["BASE_LIB"];
4 $BASE_DIR=$_SERVER["BASE_DIR"];
5 $BASE_DATA=$_SERVER["BASE_DATA"];
6 $BACULA_USER=$_SERVER["BACULA_USER"];
7
8 global $BASE_DATA, $BASE_LIB, $BACULA_USER, $BASE_DIR;
9
10 require_once "$BASE_LIB/lib.php";
11
12 if(!isset($argv[2])) {
13         echo "Usage: ".$argv[0]." changer_name command\n";
14         echo "\tbacula-config - outputs bacula config\n";
15         echo "\tinit - inits the bacula php changer stuff\n";
16         echo "\tadd-disk - adds a disk\n";
17         echo "\tdisks - lists currently (known) disks\n";
18         echo "\tstatus - prints status (whats where in which slot, drive, etc)\n";
19         exit(0);
20 }
21
22 $command = $argv[2];
23
24 switch($command) {
25         case "init":
26                 init();
27                 break;
28         case "bacula-config":
29                 bacula_config();
30                 break;
31         case "add-disk":
32                 add_disk();
33                 break;
34         case "disks":
35                 list_disks();
36                 break;
37         case "status":
38                 pbdc_status();
39                 break;
40         default:
41                 echo "invalid command\n";
42 }
43
44 function bacula_config()
45 {
46         global $BASE_DATA, $BASE_LIB, $BACULA_USER, $argv, $BASE_DIR;
47         
48         check_init_and_fail($argv[1]);
49         $changer = $argv[1];
50
51         $db = db_getDB($argv[1]);
52         
53         $ntapes = conf_getVal($changer, "ndrives");
54         $tapeloc = conf_getVal($changer, "drivelocation");
55         $tapesz = conf_getVal($changer, "tapesize");
56         
57         // TODO: do this bit
58         // bacula-sd.conf
59         ?>
60 -------------------------------- bacula-sd.conf -----------------------------
61 Autochanger {
62   Name = <?php echo "$changer\n"?>
63 <?php
64   for($i=0; $i<$ntapes; $i++) {
65         echo "  Device = \"$changer-drive$i\"\n";
66   } 
67 ?>
68   Changer Command = "<?php echo $BASE_DIR."/bin/pbdc-bacula"?> %c %o %S %a %d"
69   Changer Device = "<?php echo $changer ?>"
70 }
71
72 <?php 
73         for($i=0; $i < $ntapes; $i++) {
74 ?>
75 Device {
76   Name = <?php echo "\"$changer-drive$i\"\n" ?>
77   DriveIndex = <?php echo "$i\n" ?>
78   Autochanger = yes;
79   DeviceType = File
80   MediaType = File
81   ArchiveDevice = <?php echo "\"$tapeloc/$changer-drive$i\"\n" ?>
82   RemovableMedia = no;
83   RandomAccess = yes;
84 }
85
86
87 <?php
88         } 
89 ?>
90
91
92 -------------------------------- bacula-dir.conf -----------------------------
93 Storage {
94   Name = <INSERT STORAGE NAME>
95   Address = <ADDRESS OF WHERE SD IS RUNNING>
96   SDPort = 9103
97   Password = "<SD PASSWORD>"
98   Device = <?php echo "\"$changer-drive$i\"\n" ?>
99   Media Type = File
100   Autochanger = yes;
101 }
102
103 # a pool for our storage
104 Pool {
105   Name = <POOL NAME GOES HERE>
106   Pool Type = Backup
107   Recycle = yes                        # Bacula can automatically recycle Volumes
108   AutoPrune = yes                      # Prune expired volumes
109   Volume Retention = 50 days           # I set this to 50 days, dont ask me why
110   Maximum Volume Bytes = <?php echo "$tapesz"."G"?>           # Limit Volume size to something reasonable
111   Maximum Volumes = 1000               # Limit number of Volumes in Pool SET THIS
112 }
113
114 # add a default job job defs for our pool - modify where necessary
115 # i.e. look at the normal jobdefs, or modify that with your backup defs.
116 JobDefs {
117   Name = "PBDCJobDefs"
118   Type = Backup
119   Level = Incremental
120   Storage = <INSERT STORAGE NAME>
121   Messages = Standard
122   Pool = <POOL NAME GOES HERE>
123   Priority = 10
124   Write Bootstrap = "/var/lib/bacula/%c.bsr"
125 }
126
127
128 <?php
129
130 }
131
132 function pbdc_status()
133 {
134         global $BASE_DATA, $BASE_LIB, $BACULA_USER, $argv;
135         
136         check_init_and_fail($argv[1]);
137         $changer = $argv[1];
138
139         $db = db_getDB($argv[1]);
140         
141         $res = $db->query("select * from disk_list");
142         
143         $tapeloc = conf_getVal($changer, "drivelocation");
144         $amloc = conf_getVal($changer, "automountdir");
145         
146         $ret = false;
147         foreach($res as $row) {
148                 echo "Listing tapes for disk ".$row["disk_id"].", ".$row["disk_name"]."\n";
149                 //echo "dir: $amloc/".$row["disk_name"]."/pbdc/".$argv[1]."/tapes/\n";
150                 //exit(0);
151                 $dh = opendir("$amloc/".$row["disk_name"]."/pbdc/".$argv[1]."/tapes/");
152                 while(($file = readdir($dh)) !== false) {
153                         if(ereg("d[0-9]+_vol[0-9]+", $file)!=false) {
154                                 $sql = "select slot_no from slots where tape_name=='$file'";
155                                 $res2 = $db->query($sql);
156                                 if($res2) {
157                                         foreach($res2 as $row2)
158                                         $slot = $row2["slot_no"];
159                                 } else $slot = "none";
160                                 echo "Tape: $file (slot $slot)\n";
161                         } else {
162                                 //echo "didnt match: $file\n";
163                         }
164                         //exit(0);
165                 }
166                 closedir($dh);
167         }
168         
169         $nt = (int)(conf_getVal($changer, "ndrives"));
170         for($i = 0; $i < $nt; $i++) {
171                 if(file_exists("$tapeloc/$changer-drive$i")) {
172                         $rl = basename(readlink("$tapeloc/$changer-drive$i"));
173                         echo "drive $i points has tape $rl loaded\n";
174                 } else {
175                         echo "drive $i is unloaded\n";
176                 }
177         }
178 }
179
180 function list_disks()
181 {
182         global $BASE_DATA, $BASE_LIB, $BACULA_USER, $argv;
183         
184         check_init_and_fail($argv[1]);
185         
186         $db = db_getDB($argv[1]);
187         
188         $res = $db->query("select * from disk_list");
189         
190         $ret = false;
191         foreach($res as $row) {
192                 echo "Disk ".$row["disk_id"].": ".$row["disk_name"]."\n";
193                 $ret = true;    
194         }
195         
196         if(!$ret) {
197                 echo "No disks defined yet for this changer\n";
198         }
199         
200         
201         return;
202 }
203
204 function init()
205 {
206         global $BASE_DATA, $BASE_LIB, $BACULA_USER, $argv;
207         
208         // check if we are the bacula user
209         //$user = posix_getlogin();
210         $user = $_SERVER["USER"];
211         if($user != $BACULA_USER) {
212                 echo "This command must be run as the bacula user ($BACULA_USER not $user)\n";
213                 exit(0);
214         }
215         
216         $changer_name = $argv[1];
217         
218         echo "This command will init the data for $changer_name\n";
219         
220         if(file_exists("$BASE_DATA/$changer_name.db")&&check_init($changer_name)) {
221                 //echo "Daemon is already init'd, are you sure you wish to do this, it will loose all config [y/n]:";
222                 $r = readline("Daemon is already init'd, are you sure you wish to do this, it will loose all config [y/n]:");
223                 if($r == "y") {
224                         echo "Ok, but its your disaster, waiting 5 seconds prior to init'ing the database (hit ctrl-c to exit)\n";
225                         sleep(5);
226                         unlink("$BASE_DATA/$changer_name.db");
227                 } else {
228                         echo "Ok, not doing it, so long chum\n";
229                         exit(0);
230                 }
231         }
232         
233         echo "Creating database\n";
234         $ra = readline("Directory where automount occurs [/changer/]:");
235         $rb = readline("Number of drives [2]:");
236         $rc = readline("Size of tapes [20] (in gb):");
237         $rd = readline("Where to store drive pointers [/var/run/bacula/]:");
238         
239         if($ra == "") $ra = "/changer/";
240         if($rb == "") $rb = 2;
241         if($rc == "") $rc = 20;
242         if($rd == "") $rd = "/var/run/bacula/";
243         
244         conf_setVal($changer_name, "automountdir", "$ra");
245         conf_setVal($changer_name, "ndrives", "$rb");
246         conf_setVal($changer_name, "tapesize", "$rc");
247         conf_setVal($changer_name, "drivelocation", "$rd");
248         
249         // check if they were set
250         // echo "got: ".conf_getVal($changer_name, "automountdir").", and ".conf_getVal($changer_name, "tapesize").", and ".conf_getVal($changer_name, "ndrives")."\n";
251 }
252
253 function check_init_and_fail($changer)
254 {
255         $lk = conf_getVal($changer, "automountdir");
256         if(!$lk) {
257                 echo "DB not init'd yet, please run init first\n";
258                 exit(0);
259         } 
260 }
261
262 function check_init($changer)
263 {
264         $lk = conf_getVal($changer, "automountdir");
265         if(!$lk) {
266                 return false;
267         } 
268         
269         return true;
270 }
271
272 function disk_get_size($file, $realname="")
273 {
274         global $BASE_DATA, $BASE_LIB, $BACULA_USER, $argv;
275         
276         check_init_and_fail($argv[1]);
277
278         $changer = $argv[1];
279         
280         error_reporting(E_ALL);
281         
282         $dh = opendir("/dev/disk/by-uuid");
283         $amdir = conf_getVal($changer, "automountdir");
284         
285         $ts = 0;
286
287         $parsed = false;
288         if(file_exists("/$amdir/$file/.")) {
289                 $cmd = "/bin/df -k /$amdir/$file/. |/usr/bin/tail -1  |/usr/bin/awk '{ print $4 }'";
290                 $pl = popen($cmd, "r");
291                 if($pl) {
292                         $f = fread($pl, 1024);
293                         //echo "cmd: $cmd\nf: $f\n";
294                         $ts = (int)($f);
295                         if($ts > 1) $parsed = true;
296                         pclose($pl);
297                 }
298         }
299         
300         if(!$parsed && $realname != "") {
301                 $fh = fopen("/sys/class/block/$realname/size", "r");
302                 $lk = ((int)(fgets($fh)));
303                 $ts = ($lk/1024)*512;
304                 fclose($fh);
305                 
306         }
307         
308         $realsize = (int)(($ts/1024)/1024);
309
310         return $realsize;
311 }
312
313 function add_disk()
314 {
315         // disks get inited by going to /changer_dir/disk_uuid/ then:
316         // creating pbdc/changer_name/stuff.
317         global $BASE_DATA, $BASE_LIB, $BACULA_USER, $argv;
318         
319         check_init_and_fail($argv[1]);
320         
321         $changer = $argv[1];
322         
323         $dh = opendir("/dev/disk/by-uuid");
324         $amdir = conf_getVal($changer, "automountdir");
325         
326         $disk = "";
327         $i = 0;
328         
329         while(($file = readdir($dh)) !== false) {
330                 if($file != "." && $file != "..") {
331                         $st = stat("/dev/disk/by-uuid/".$file);
332                         $realname = basename(readlink("/dev/disk/by-uuid/".$file));
333                         $realsize = disk_get_size($file, $realname);
334                         
335                         // now get the size, first lets try and parse df output
336                         echo "disk $i: /dev/$realname or /dev/disk/by-uuid/$file of size ".$realsize."gb\n";
337                         $disk[$i]["real"] = "/dev/$realname";
338                         $disk[$i]["syml"] = "/dev/disk/by-uuid/".$file;
339                         $disk[$i]["size"] = $realsize;
340                         $i++;
341                         
342                 }
343         }
344         
345         closedir($dh);
346         
347         $i = readline("Choose a disk from the list above:");
348         if(isset($disk[$i]["real"])) {
349                 echo "you have chosen, ".$disk[$i]["syml"]." (".$disk[$i]["real"].") of size ".$disk[$i]["size"]."gb\n";
350                 $ans = readline("is this correct? [y/n]:");
351                 if($ans != "y") {
352                         echo "you said no, i bail\n";
353                         exit(0);
354                 }
355         } else {
356                 echo "invalid selection\n";
357                 exit(0);
358         }
359
360         // now we try and init the disk
361         // we have to get automount directory config
362         $dir = conf_getVal($argv[1], "automountdir");
363         if(!is_dir($dir)) {
364                 echo "cant find automount directory, $dir\n";
365         }
366         $mkd = "$dir/".basename($disk[$i]["syml"])."/pbdc/".$argv[1]."/tapes/";
367         $k = mkdir($mkd, 0700, true);
368         
369         if(!is_dir($mkd)) {
370                 echo "Error, couldn't create directory, check permissions on $mkd?\n";
371         }
372         
373         $ts = conf_getVal($argv[1], "tapesize");
374         $max = (int)($disk[$i]["size"]/$ts);
375         
376         $kt = (int)(readline("No of tapes to create [max:$max] @ ".$ts."gb each:"));
377         if($kt > $max) {
378                 echo "Sorry, max number of tapes is $max, creating $max instead\n";
379                 $kt = $max;
380         }
381         
382         $did = add_diskToDB($argv[1], basename($disk[$i]["syml"]));
383         
384         if(!$did) {
385                 echo "Disk already exists, doing nothing\n";
386                 exit(0);
387         }
388         
389         // disk names are d.$did_vol0000x
390         //echo "did: $did\n";
391         for($ii=0; $ii < $kt; $ii++) {
392                 $tid = sprintf("d%d_vol%04d", $did, $ii);
393                 //echo "would create $tid\n";
394                 $tp = "$dir/".basename($disk[$i]["syml"])."/pbdc/".$argv[1]."/tapes/$tid";
395                 if(!file_exists($tp)) {
396                         fopen($tp, "w");
397                         echo "created tape $tid\n";
398                 } else {
399                         echo "tape $tid already existed\n";
400                 }
401                 add_to_slot($argv[1], $tid, $did);
402         }
403 }
404
405 function add_to_slot($changer, $tapename, $disk_id)
406 {
407         global $BASE_DATA, $BASE_LIB, $BACULA_USER, $argv;
408         
409         $db = db_getDB($changer);
410         
411         $slot = "";
412         $slots = 0;
413         $max_slot = 0;
414         
415         $res = $db->query("select * from slots");
416         foreach($res as $row) {
417                 $slot[$row["slot_no"]]["diskid"] = $row["disk_id"];
418                 $slot[$row["slot_no"]]["tapename"] = $row["tape_name"];
419                 $slots++;
420                 if($row["slot_no"] > $max_slot) $max_slot = $row["slot_no"];
421         }
422         
423         // first tape, straight in with you
424         if($slots == 0) {
425                 $db->query("insert into slots values (NULL, 1, $disk_id, '$tapename')");
426                 return;
427         }
428         
429         // next hunt thru the slots and see if its filled yet.
430         foreach($slot as $lk) {
431                 if($lk["tapename"] == $tapename) {
432                         // tape was already there, move along
433                         return;
434                 }
435         }
436         
437         // now we go from 0 to the end looking for a free slot
438         for($i=1; $i < $max_slot; $i++) {
439                 if(!isset($slot[$i])) {
440                         // we have a free slot, but it shouldnt appear that way like that.. oh well.
441                         $db->query("insert into slots values (NULL, $i, $disk_id, '$tapename')");
442                         return;
443                 }
444                 if($slot[$i]["tapename"] == "") {
445                         // slot is free
446                         $db->query("delete from slots where slot_no='$i'");
447                         $db->query("insert into slots values (NULL, $i, $disk_id, '$tapename')");
448                         return;
449                 }
450         }
451         
452         // So much error checking to do.
453         // if we made it here, we need more slots.
454         $ns = $max_slot + 1;
455         $db->query("insert into slots values (NULL, $ns, $disk_id, '$tapename')");
456         return;
457 }
458 ?>