]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/autochanger.c
Convert to pure GPL v2 license.
[bacula/bacula] / bacula / src / stored / autochanger.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2007 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *  Routines for handling the autochanger.
31  *
32  *   Kern Sibbald, August MMII
33  *                            
34  *   Version $Id$
35  */
36
37 #include "bacula.h"                   /* pull in global headers */
38 #include "stored.h"                   /* pull in Storage Deamon headers */
39
40 /* Forward referenced functions */
41 static void lock_changer(DCR *dcr);
42 static void unlock_changer(DCR *dcr);
43 static bool unload_other_drive(DCR *dcr, int slot);
44
45 /* Init all the autochanger resources found */
46 bool init_autochangers()
47 {
48    bool OK = true;
49    AUTOCHANGER *changer;
50    /* Ensure that the media_type for each device is the same */
51    foreach_res(changer, R_AUTOCHANGER) {
52       DEVRES *device;
53       foreach_alist(device, changer->device) {
54          /*
55           * If the device does not have a changer name or changer command
56           *   defined, used the one from the Autochanger resource 
57           */
58          if (!device->changer_name && changer->changer_name) {
59             device->changer_name = bstrdup(changer->changer_name);
60          }
61          if (!device->changer_command && changer->changer_command) {
62             device->changer_command = bstrdup(changer->changer_command);
63          }
64          if (!device->changer_name) {
65             Jmsg(NULL, M_ERROR, 0, 
66                _("No Changer Name given for device %s. Cannot continue.\n"),
67                device->hdr.name);
68             OK = false;
69          }   
70          if (!device->changer_command) {
71             Jmsg(NULL, M_ERROR, 0, 
72                _("No Changer Command given for device %s. Cannot continue.\n"),
73                device->hdr.name);
74             OK = false;
75          }   
76
77 #ifdef xxx_needed
78          if (media_type == NULL) {
79             media_type = device->media_type;     /* get Media Type of first device */
80             continue;
81          }     
82          /* Ensure that other devices Media Types are the same */
83          if (strcmp(media_type, device->media_type) != 0) {
84             Jmsg(NULL, M_ERROR, 0, 
85                _("Media Type not the same for all devices in changer %s. Cannot continue.\n"),
86                changer->hdr.name);
87             OK = false;
88             continue;
89          }
90 #endif
91       }
92    }
93    return OK;
94 }
95
96
97 /*
98  * Called here to do an autoload using the autochanger, if
99  *  configured, and if a Slot has been defined for this Volume.
100  *  On success this routine loads the indicated tape, but the
101  *  label is not read, so it must be verified.
102  *
103  *  Note if dir is not NULL, it is the console requesting the
104  *   autoload for labeling, so we respond directly to the
105  *   dir bsock.
106  *
107  *  Returns: 1 on success
108  *           0 on failure (no changer available)
109  *          -1 on error on autochanger
110  */
111 int autoload_device(DCR *dcr, int writing, BSOCK *dir)
112 {
113    JCR *jcr = dcr->jcr;
114    DEVICE *dev = dcr->dev;
115    int slot;
116    int drive = dev->drive_index;
117    int rtn_stat = -1;                 /* error status */
118    POOLMEM *changer;
119
120    if (!dev->is_autochanger()) {
121       Dmsg1(200, "Device %s is not an autochanger\n", dev->print_name());
122       return 0;
123    }
124
125    /* An empty ChangerCommand => virtual disk autochanger */
126    if (dcr->device->changer_command && dcr->device->changer_command[0] == 0) {
127       return 1;                       /* nothing to load */
128    }
129
130    slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
131    /*
132     * Handle autoloaders here.  If we cannot autoload it, we
133     *  will return 0 so that the sysop will be asked to load it.
134     */
135    if (writing && slot <= 0) {
136       if (dir) {
137          return 0;                    /* For user, bail out right now */
138       }
139       if (dir_find_next_appendable_volume(dcr)) {
140          slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
141       } else {
142          slot = 0;
143       }
144    }
145    Dmsg1(400, "Want changer slot=%d\n", slot);
146
147    changer = get_pool_memory(PM_FNAME);
148    if (slot <= 0) {
149       Jmsg(jcr, M_INFO, 0, _("Invalid slot=%d defined in catalog. Manual load my be required.\n"), slot);
150       rtn_stat = 0;
151    } else if (!dcr->device->changer_name) {
152       Jmsg(jcr, M_INFO, 0, _("No \"Changer Device\" manual load of Volume may be required.\n"));
153       rtn_stat = 0;
154   } else if (!dcr->device->changer_command) {
155       Jmsg(jcr, M_INFO, 0, _("No \"Changer Command\" manual load of Volume may be requird.\n"));
156       rtn_stat = 0;
157   } else {
158       /* Attempt to load the Volume */
159
160       uint32_t timeout = dcr->device->max_changer_wait;
161       int loaded, status;
162
163       loaded = get_autochanger_loaded_slot(dcr);
164
165       if (loaded != slot) {
166          POOL_MEM results(PM_MESSAGE);
167
168          /* Unload anything in our drive */
169          if (!unload_autochanger(dcr, loaded)) {
170             goto bail_out;
171          }
172             
173          /* Make sure desired slot is unloaded */
174          if (!unload_other_drive(dcr, slot)) {
175             goto bail_out;
176          }
177
178          /*
179           * Load the desired cassette
180           */
181          lock_changer(dcr);
182          Dmsg1(100, "Doing changer load slot %d\n", slot);
183          Jmsg(jcr, M_INFO, 0,
184               _("3304 Issuing autochanger \"load slot %d, drive %d\" command.\n"),
185               slot, drive);
186          dcr->VolCatInfo.Slot = slot;    /* slot to be loaded */
187          changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "load");
188          dev->close();
189          Dmsg1(200, "Run program=%s\n", changer);
190          status = run_program_full_output(changer, timeout, results.c_str());
191          if (status == 0) {
192             Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"),
193                     slot, drive);
194             Dmsg2(100, "load slot %d, drive %d, status is OK.\n", slot, drive);
195             dev->Slot = slot;         /* set currently loaded slot */
196          } else {
197             berrno be;
198             be.set_errno(status);
199             Dmsg3(100, "load slot %d, drive %d, bad stats=%s.\n", slot, drive,
200                be.bstrerror());
201             Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": "
202                  "ERR=%s.\nResults=%s\n"),
203                     slot, drive, be.bstrerror(), results.c_str());
204             rtn_stat = -1;            /* hard error */
205             dev->Slot = -1;           /* mark unknown */
206          }
207          Dmsg2(100, "load slot %d status=%d\n", slot, status);
208          unlock_changer(dcr);
209       } else {
210          status = 0;                  /* we got what we want */
211          dev->Slot = slot;            /* set currently loaded slot */
212       }
213       Dmsg1(100, "After changer, status=%d\n", status);
214       if (status == 0) {              /* did we succeed? */
215          rtn_stat = 1;                /* tape loaded by changer */
216       }
217    }
218    free_pool_memory(changer);
219    return rtn_stat;
220
221 bail_out:
222    free_pool_memory(changer);
223    return -1;
224
225 }
226
227 /*
228  * Returns: -1 if error from changer command
229  *          slot otherwise
230  *  Note, this is safe to do without releasing the drive
231  *   since it does not attempt load/unload a slot.
232  */
233 int get_autochanger_loaded_slot(DCR *dcr)
234 {
235    JCR *jcr = dcr->jcr;
236    DEVICE *dev = dcr->dev;
237    int status, loaded;
238    uint32_t timeout = dcr->device->max_changer_wait;
239    int drive = dcr->dev->drive_index;
240    POOL_MEM results(PM_MESSAGE);
241    POOLMEM *changer;
242
243    if (!dev->is_autochanger()) {
244       return -1;
245    }
246    if (!dcr->device->changer_command) {
247       Jmsg(jcr, M_FATAL, 0, _("3992 Missing Changer command.\n"));
248       return -1;
249    }
250    if (dev->Slot > 0) {
251       return dev->Slot;
252    }
253    /* Virtual disk autochanger */
254    if (dcr->device->changer_command[0] ==0) {
255       return 1;
256    }
257
258    /* Find out what is loaded, zero means device is unloaded */
259    changer = get_pool_memory(PM_FNAME);
260    lock_changer(dcr);
261    Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded? drive %d\" command.\n"),
262         drive);
263    changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded");
264    *results.c_str() = 0;
265    Dmsg1(100, "Run program=%s\n", changer);
266    status = run_program_full_output(changer, timeout, results.c_str());
267    Dmsg3(100, "run_prog: %s stat=%d result=%s", changer, status, results.c_str());
268    if (status == 0) {
269       loaded = str_to_int32(results.c_str());
270       if (loaded > 0) {
271          Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result is Slot %d.\n"),
272               drive, loaded);
273          dev->Slot = loaded;
274       } else {
275          Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result: nothing loaded.\n"),
276               drive);
277          dev->Slot = -1;    /* unknown */
278       }
279    } else {
280       berrno be;
281       be.set_errno(status);
282       Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded? drive %d\" command: "
283            "ERR=%s.\nResults=%s\n"), drive, be.bstrerror(), results.c_str());
284       loaded = -1;              /* force unload */
285    }
286    unlock_changer(dcr);
287    free_pool_memory(changer);
288    return loaded;
289 }
290
291 static void lock_changer(DCR *dcr)
292 {
293    AUTOCHANGER *changer_res = dcr->device->changer_res;
294    if (changer_res) {
295       Dmsg1(200, "Locking changer %s\n", changer_res->hdr.name);
296       P(changer_res->changer_mutex);  /* Lock changer script */
297    }
298 }
299
300 static void unlock_changer(DCR *dcr)
301 {
302    AUTOCHANGER *changer_res = dcr->device->changer_res;
303    if (changer_res) {
304       Dmsg1(200, "Unlocking changer %s\n", changer_res->hdr.name);
305       V(changer_res->changer_mutex);  /* Unlock changer script */
306    }
307 }
308
309 /*
310  * Unload the volume, if any, in this drive
311  *  On entry: loaded == 0 -- nothing to do
312  *            loaded  < 0 -- check if anything to do
313  *            loaded  > 0 -- load slot == loaded
314  */
315 bool unload_autochanger(DCR *dcr, int loaded)
316 {
317    DEVICE *dev = dcr->dev;
318    JCR *jcr = dcr->jcr;
319    int slot;
320    uint32_t timeout = dcr->device->max_changer_wait;
321    bool ok = true;
322
323    if (loaded == 0) {
324       return true;
325    }
326
327    if (!dev->is_autochanger() || !dcr->device->changer_name ||
328        !dcr->device->changer_command) {
329       return false;
330    }
331
332    if (loaded < 0) {
333       loaded = get_autochanger_loaded_slot(dcr);
334    }
335
336    if (loaded > 0) {
337       POOL_MEM results(PM_MESSAGE);
338       POOLMEM *changer = get_pool_memory(PM_FNAME);
339       lock_changer(dcr);
340       Jmsg(jcr, M_INFO, 0,
341            _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
342            loaded, dev->drive_index);
343       slot = dcr->VolCatInfo.Slot;
344       dcr->VolCatInfo.Slot = loaded;
345       changer = edit_device_codes(dcr, changer, 
346                    dcr->device->changer_command, "unload");
347       dev->close();
348       Dmsg1(100, "Run program=%s\n", changer);
349       *results.c_str() = 0;
350       int stat = run_program_full_output(changer, timeout, results.c_str());
351       dcr->VolCatInfo.Slot = slot;
352       if (stat != 0) {
353          berrno be;
354          be.set_errno(stat);
355          Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": "
356               "ERR=%s\nResults=%s\n"),
357                  loaded, dev->drive_index, be.bstrerror(), results.c_str());
358          ok = false;
359          dev->Slot = -1;           /* unknown */
360       } else {
361          dev->Slot = 0;            /* nothing loaded */
362       }
363       free_pool_memory(changer);
364       unlock_changer(dcr);
365    }
366    return ok;
367 }
368
369 /*
370  * Unload the slot if mounted in a different drive
371  */
372 static bool unload_other_drive(DCR *dcr, int slot)
373 {
374    DEVICE *dev = NULL;
375    DEVICE *save_dev;
376    JCR *jcr = dcr->jcr;
377    int save_slot;
378    uint32_t timeout = dcr->device->max_changer_wait;
379    bool ok = true;
380    AUTOCHANGER *changer = dcr->dev->device->changer_res;
381    DEVRES *device;
382    bool found = false;
383    int retries = 0;                /* wait for device retries */
384
385    if (!changer) {
386       return false;
387    }
388    if (changer->device->size() == 1) {
389       return true;
390    }
391
392    foreach_alist(device, changer->device) {
393       if (device->dev && device->dev->Slot == slot) {
394          found = true;
395          dev = device->dev;
396          break;
397       }
398    }
399    if (!found) {
400       return true;
401    }
402
403    /* The Volume we want is on another device. */
404    if (dev->is_busy()) {
405       Dmsg4(100, "Vol %s for dev=%s in use dev=%s slot=%d\n",
406            dcr->VolumeName, dcr->dev->print_name(),
407            dev->print_name(), slot);
408    }   
409    for (int i=0; i < 3; i++) {
410       if (dev->is_busy()) {
411          wait_for_device(dcr->jcr, retries);
412          continue;
413       }
414       break;
415    }
416    dev->dlock();
417    if (dev->is_busy()) {
418       Jmsg(jcr, M_WARNING, 0, _("Volume \"%s\" is in use by device %s\n"),
419            dcr->VolumeName, dev->print_name());
420       Dmsg4(100, "Vol %s for dev=%s is busy dev=%s slot=%d\n",
421            dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), slot);
422       Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->reserved_device);
423       dev->dunlock();
424       return false;
425    }
426
427    POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
428    POOL_MEM results(PM_MESSAGE);
429    lock_changer(dcr);
430    Jmsg(jcr, M_INFO, 0,
431         _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
432         slot, dev->drive_index);
433
434    Dmsg2(100, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
435         slot, dev->drive_index);
436
437    save_dev = dcr->dev;
438    dcr->dev = dev;
439    save_slot = dcr->VolCatInfo.Slot;
440    dcr->VolCatInfo.Slot = slot;
441    changer_cmd = edit_device_codes(dcr, changer_cmd, 
442                 dcr->device->changer_command, "unload");
443    dev->close();
444    Dmsg2(200, "close dev=%s reserve=%d\n", dev->print_name(), 
445       dev->reserved_device);
446    Dmsg1(100, "Run program=%s\n", changer_cmd);
447    int stat = run_program_full_output(changer_cmd, timeout, results.c_str());
448    dcr->VolCatInfo.Slot = save_slot;
449    dcr->dev = save_dev;
450    if (stat != 0) {
451       berrno be;
452       be.set_errno(stat);
453       Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
454               slot, dev->drive_index, be.bstrerror());
455
456       Dmsg3(100, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
457               slot, dev->drive_index, be.bstrerror());
458       ok = false;
459       dev->Slot = -1;          /* unknown */
460    } else {
461       dev->Slot = 0;           /* nothing loaded */
462       Dmsg0(100, "Slot unloaded\n");
463    }
464    unlock_changer(dcr);
465    dev->dunlock();
466    free_pool_memory(changer_cmd);
467    return ok;
468 }
469
470
471
472 /*
473  * List the Volumes that are in the autoloader possibly
474  *   with their barcodes.
475  *   We assume that it is always the Console that is calling us.
476  */
477 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)  
478 {
479    DEVICE *dev = dcr->dev;
480    uint32_t timeout = dcr->device->max_changer_wait;
481    POOLMEM *changer;
482    BPIPE *bpipe;
483    int len = sizeof_pool_memory(dir->msg) - 1;
484    bool ok = false;
485    int stat;
486
487    if (!dev->is_autochanger() || !dcr->device->changer_name ||
488        !dcr->device->changer_command) {
489       if (strcmp(cmd, "drives") == 0) {
490          bnet_fsend(dir, "drives=1\n");
491       }
492       bnet_fsend(dir, _("3993 Device %s not an autochanger device.\n"),
493          dev->print_name());
494       return false;
495    }
496
497    if (strcmp(cmd, "drives") == 0) {
498       AUTOCHANGER *changer_res = dcr->device->changer_res;
499       int drives = 1;
500       if (changer_res) {
501          drives = changer_res->device->size();
502       }
503       bnet_fsend(dir, "drives=%d\n", drives);
504       Dmsg1(100, "drives=%d\n", drives);
505       return true;
506    }
507
508    changer = get_pool_memory(PM_FNAME);
509    lock_changer(dcr);
510    /* Now issue the command */
511    changer = edit_device_codes(dcr, changer, 
512                  dcr->device->changer_command, cmd);
513    bnet_fsend(dir, _("3306 Issuing autochanger \"%s\" command.\n"), cmd);
514    bpipe = open_bpipe(changer, timeout, "r");
515    if (!bpipe) {
516       bnet_fsend(dir, _("3996 Open bpipe failed.\n"));
517       goto bail_out;
518    }
519    if (strcmp(cmd, "list") == 0) {
520       /* Get output from changer */
521       while (fgets(dir->msg, len, bpipe->rfd)) {
522          dir->msglen = strlen(dir->msg);
523          Dmsg1(100, "<stored: %s\n", dir->msg);
524          bnet_send(dir);
525       }
526    } else if (strcmp(cmd, "slots") == 0 ) {
527       char buf[100], *p;
528       /* For slots command, read a single line */
529       buf[0] = 0;
530       fgets(buf, sizeof(buf)-1, bpipe->rfd);
531       buf[sizeof(buf)-1] = 0;
532       /* Strip any leading space in front of # of slots */
533       for (p=buf; B_ISSPACE(*p); p++)
534         { }
535       bnet_fsend(dir, "slots=%s", p);
536       Dmsg1(100, "<stored: %s", dir->msg);
537    } 
538                  
539    stat = close_bpipe(bpipe);
540    if (stat != 0) {
541       berrno be;
542       be.set_errno(stat);
543       bnet_fsend(dir, _("Autochanger error: ERR=%s\n"), be.bstrerror());
544    }
545    bnet_sig(dir, BNET_EOD);
546    ok = true;
547
548 bail_out:
549    unlock_changer(dcr);
550    free_pool_memory(changer);
551    return true;
552 }
553
554
555 /*
556  * Edit codes into ChangerCommand
557  *  %% = %
558  *  %a = archive device name
559  *  %c = changer device name
560  *  %d = changer drive index
561  *  %f = Client's name
562  *  %j = Job name
563  *  %o = command
564  *  %s = Slot base 0
565  *  %S = Slot base 1
566  *  %v = Volume name
567  *
568  *
569  *  omsg = edited output message
570  *  imsg = input string containing edit codes (%x)
571  *  cmd = command string (load, unload, ...)
572  *
573  */
574 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
575 {
576    const char *p;
577    const char *str;
578    char add[20];
579
580    *omsg = 0;
581    Dmsg1(1800, "edit_device_codes: %s\n", imsg);
582    for (p=imsg; *p; p++) {
583       if (*p == '%') {
584          switch (*++p) {
585          case '%':
586             str = "%";
587             break;
588          case 'a':
589             str = dcr->dev->archive_name();
590             break;
591          case 'c':
592             str = NPRT(dcr->device->changer_name);
593             break;
594          case 'd':
595             sprintf(add, "%d", dcr->dev->drive_index);
596             str = add;
597             break;
598          case 'o':
599             str = NPRT(cmd);
600             break;
601          case 's':
602             sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
603             str = add;
604             break;
605          case 'S':
606             sprintf(add, "%d", dcr->VolCatInfo.Slot);
607             str = add;
608             break;
609          case 'j':                    /* Job name */
610             str = dcr->jcr->Job;
611             break;
612          case 'v':
613             str = NPRT(dcr->VolumeName);
614             break;
615          case 'f':
616             str = NPRT(dcr->jcr->client_name);
617             break;
618
619          default:
620             add[0] = '%';
621             add[1] = *p;
622             add[2] = 0;
623             str = add;
624             break;
625          }
626       } else {
627          add[0] = *p;
628          add[1] = 0;
629          str = add;
630       }
631       Dmsg1(1900, "add_str %s\n", str);
632       pm_strcat(&omsg, (char *)str);
633       Dmsg1(1800, "omsg=%s\n", omsg);
634    }
635    Dmsg1(800, "omsg=%s\n", omsg);
636    return omsg;
637 }