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