]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/autochanger.c
ebl First cup of faketape dummy tape driver for regression testing.
[bacula/bacula] / bacula / src / stored / autochanger.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2008 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 * volatile 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(100, "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       Dmsg0(100, "ChangerCommand=0, virtual disk changer\n");
128       return 1;                       /* nothing to load */
129    }
130
131    slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
132    Dmsg3(100, "autoload: slot=%d InChgr=%d Vol=%s\n", dcr->VolCatInfo.Slot,
133          dcr->VolCatInfo.InChanger, dcr->VolCatInfo.VolCatName);
134    /*
135     * Handle autoloaders here.  If we cannot autoload it, we
136     *  will return 0 so that the sysop will be asked to load it.
137     */
138    if (writing && slot <= 0) {
139       if (dir) {
140          return 0;                    /* For user, bail out right now */
141       }
142       /* ***FIXME*** this really should not be here */
143       if (dir_find_next_appendable_volume(dcr)) {
144          slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
145       } else {
146          slot = 0;
147       }
148    }
149    Dmsg1(400, "Want changer slot=%d\n", slot);
150
151    changer = get_pool_memory(PM_FNAME);
152    if (slot <= 0) {
153       Jmsg(jcr, M_INFO, 0, _("Invalid slot=%d defined in catalog for Volume \"%s\" "
154            "on %s. Manual load may be required.\n"), slot, dcr->VolCatInfo.VolCatName,
155            dev->print_name());
156       rtn_stat = 0;
157    } else if (!dcr->device->changer_name) {
158       Jmsg(jcr, M_INFO, 0, _("No \"Changer Device\" for %s. Manual load of Volume may be required.\n"),
159            dev->print_name());
160       rtn_stat = 0;
161   } else if (!dcr->device->changer_command) {
162       Jmsg(jcr, M_INFO, 0, _("No \"Changer Command\" for %s. Manual load of Volume may be requird.\n"),
163            dev->print_name());
164       rtn_stat = 0;
165   } else {
166       /* Attempt to load the Volume */
167
168       uint32_t timeout = dcr->device->max_changer_wait;
169       int loaded, status;
170
171       loaded = get_autochanger_loaded_slot(dcr);
172
173       if (loaded != slot) {
174          POOL_MEM results(PM_MESSAGE);
175
176          /* Unload anything in our drive */
177          if (!unload_autochanger(dcr, loaded)) {
178             goto bail_out;
179          }
180             
181          /* Make sure desired slot is unloaded */
182          if (!unload_other_drive(dcr, slot)) {
183             goto bail_out;
184          }
185
186          /*
187           * Load the desired cassette
188           */
189          lock_changer(dcr);
190          Dmsg1(100, "Doing changer load slot %d\n", slot);
191          Jmsg(jcr, M_INFO, 0,
192               _("3304 Issuing autochanger \"load slot %d, drive %d\" command.\n"),
193               slot, drive);
194          dcr->VolCatInfo.Slot = slot;    /* slot to be loaded */
195          changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "load");
196          dev->close();
197          Dmsg1(200, "Run program=%s\n", changer);
198          status = run_program_full_output(changer, timeout, results.addr());
199          if (status == 0) {
200             Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"),
201                     slot, drive);
202             Dmsg2(100, "load slot %d, drive %d, status is OK.\n", slot, drive);
203             dev->Slot = slot;         /* set currently loaded slot */
204          } else {
205             berrno be;
206             be.set_errno(status);
207             Dmsg3(100, "load slot %d, drive %d, bad stats=%s.\n", slot, drive,
208                be.bstrerror());
209             Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": "
210                  "ERR=%s.\nResults=%s\n"),
211                     slot, drive, be.bstrerror(), results.c_str());
212             rtn_stat = -1;            /* hard error */
213             dev->Slot = -1;           /* mark unknown */
214          }
215          Dmsg2(100, "load slot %d status=%d\n", slot, status);
216          unlock_changer(dcr);
217       } else {
218          status = 0;                  /* we got what we want */
219          dev->Slot = slot;            /* set currently loaded slot */
220       }
221       Dmsg1(100, "After changer, status=%d\n", status);
222       if (status == 0) {              /* did we succeed? */
223          rtn_stat = 1;                /* tape loaded by changer */
224       }
225    }
226    free_pool_memory(changer);
227    return rtn_stat;
228
229 bail_out:
230    free_pool_memory(changer);
231    return -1;
232
233 }
234
235 /*
236  * Returns: -1 if error from changer command
237  *          slot otherwise
238  *  Note, this is safe to do without releasing the drive
239  *   since it does not attempt load/unload a slot.
240  */
241 int get_autochanger_loaded_slot(DCR *dcr)
242 {
243    JCR *jcr = dcr->jcr;
244    DEVICE *dev = dcr->dev;
245    int status, loaded;
246    uint32_t timeout = dcr->device->max_changer_wait;
247    int drive = dcr->dev->drive_index;
248    POOL_MEM results(PM_MESSAGE);
249    POOLMEM *changer;
250
251    if (!dev->is_autochanger()) {
252       return -1;
253    }
254    if (!dcr->device->changer_command) {
255 //    Jmsg(jcr, M_FATAL, 0, _("3992 Missing Changer command.\n"));
256       return -1;
257    }
258    if (dev->Slot > 0) {
259       return dev->Slot;
260    }
261    /* Virtual disk autochanger */
262    if (dcr->device->changer_command[0] == 0) {
263       return 1;
264    }
265
266    /* Find out what is loaded, zero means device is unloaded */
267    changer = get_pool_memory(PM_FNAME);
268    lock_changer(dcr);
269    Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded? drive %d\" command.\n"),
270         drive);
271    changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded");
272    Dmsg1(100, "Run program=%s\n", changer);
273    status = run_program_full_output(changer, timeout, results.addr());
274    Dmsg3(100, "run_prog: %s stat=%d result=%s", changer, status, results.c_str());
275    if (status == 0) {
276       loaded = str_to_int32(results.c_str());
277       if (loaded > 0) {
278          Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result is Slot %d.\n"),
279               drive, loaded);
280          dev->Slot = loaded;
281       } else {
282          Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result: nothing loaded.\n"),
283               drive);
284          dev->Slot = -1;    /* unknown */
285       }
286    } else {
287       berrno be;
288       be.set_errno(status);
289       Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded? drive %d\" command: "
290            "ERR=%s.\nResults=%s\n"), drive, be.bstrerror(), results.c_str());
291       loaded = -1;              /* force unload */
292    }
293    unlock_changer(dcr);
294    free_pool_memory(changer);
295    return loaded;
296 }
297
298 static void lock_changer(DCR *dcr)
299 {
300    AUTOCHANGER *changer_res = dcr->device->changer_res;
301    if (changer_res) {
302       Dmsg1(200, "Locking changer %s\n", changer_res->hdr.name);
303       P(changer_res->changer_mutex);  /* Lock changer script */
304    }
305 }
306
307 static void unlock_changer(DCR *dcr)
308 {
309    AUTOCHANGER *changer_res = dcr->device->changer_res;
310    if (changer_res) {
311       Dmsg1(200, "Unlocking changer %s\n", changer_res->hdr.name);
312       V(changer_res->changer_mutex);  /* Unlock changer script */
313    }
314 }
315
316 /*
317  * Unload the volume, if any, in this drive
318  *  On entry: loaded == 0 -- nothing to do
319  *            loaded  < 0 -- check if anything to do
320  *            loaded  > 0 -- load slot == loaded
321  */
322 bool unload_autochanger(DCR *dcr, int loaded)
323 {
324    DEVICE *dev = dcr->dev;
325    JCR *jcr = dcr->jcr;
326    int slot;
327    uint32_t timeout = dcr->device->max_changer_wait;
328    bool ok = true;
329
330    if (loaded == 0) {
331       return true;
332    }
333
334    if (!dev->is_autochanger() || !dcr->device->changer_name ||
335        !dcr->device->changer_command) {
336       return false;
337    }
338
339    /* Virtual disk autochanger */
340    if (dcr->device->changer_command[0] == 0) {
341       return true;
342    }
343
344    if (loaded < 0) {
345       loaded = get_autochanger_loaded_slot(dcr);
346    }
347
348    if (loaded > 0) {
349       POOL_MEM results(PM_MESSAGE);
350       POOLMEM *changer = get_pool_memory(PM_FNAME);
351       lock_changer(dcr);
352       Jmsg(jcr, M_INFO, 0,
353            _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
354            loaded, dev->drive_index);
355       slot = dcr->VolCatInfo.Slot;
356       dcr->VolCatInfo.Slot = loaded;
357       changer = edit_device_codes(dcr, changer, 
358                    dcr->device->changer_command, "unload");
359       dev->close();
360       Dmsg1(100, "Run program=%s\n", changer);
361       int stat = run_program_full_output(changer, timeout, results.addr());
362       dcr->VolCatInfo.Slot = slot;
363       if (stat != 0) {
364          berrno be;
365          be.set_errno(stat);
366          Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": "
367               "ERR=%s\nResults=%s\n"),
368                  loaded, dev->drive_index, be.bstrerror(), results.c_str());
369          ok = false;
370          dev->Slot = -1;           /* unknown */
371       } else {
372          dev->Slot = 0;            /* nothing loaded */
373       }
374       dev->clear_unload();
375       free_volume(dev);            /* Free any volume associated with this drive */
376       free_pool_memory(changer);
377       unlock_changer(dcr);
378    }
379    return ok;
380 }
381
382 /*
383  * Unload the slot if mounted in a different drive
384  */
385 static bool unload_other_drive(DCR *dcr, int slot)
386 {
387    DEVICE *dev = NULL;
388    bool found = false;
389    AUTOCHANGER *changer = dcr->dev->device->changer_res;
390    DEVRES *device;
391    int retries = 0;                /* wait for device retries */
392
393    if (!changer) {
394       return false;
395    }
396    if (changer->device->size() == 1) {
397       return true;
398    }
399
400    foreach_alist(device, changer->device) {
401       if (device->dev && device->dev->Slot == slot) {
402          found = true;
403          dev = device->dev;
404          break;
405       }
406    }
407    if (!found) {
408       return true;
409    }
410
411    /* The Volume we want is on another device. */
412    if (dev->is_busy()) {
413       Dmsg4(100, "Vol %s for dev=%s in use dev=%s slot=%d\n",
414            dcr->VolumeName, dcr->dev->print_name(),
415            dev->print_name(), slot);
416    }   
417    for (int i=0; i < 3; i++) {
418       if (dev->is_busy()) {
419          wait_for_device(dcr->jcr, retries);
420          continue;
421       }
422       break;
423    }
424    if (dev->is_busy()) {
425       Jmsg(dcr->jcr, M_WARNING, 0, _("Volume \"%s\" is in use by device %s\n"),
426            dcr->VolumeName, dev->print_name());
427       Dmsg4(100, "Vol %s for dev=%s is busy dev=%s slot=%d\n",
428            dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), dev->Slot);
429       Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->num_reserved());
430       return false;
431    }
432    return unload_dev(dcr, dev);
433 }
434
435 bool unload_dev(DCR *dcr, DEVICE *dev)
436 {
437    JCR *jcr = dcr->jcr;
438    bool ok = true;
439    uint32_t timeout = dcr->device->max_changer_wait;
440    AUTOCHANGER *changer = dcr->dev->device->changer_res;
441    DEVICE *save_dev;
442    int save_slot;
443
444    if (!changer || dev->Slot <= 0) {
445       return false;
446    }
447    dev->dlock();
448
449    POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
450    POOL_MEM results(PM_MESSAGE);
451    lock_changer(dcr);
452    Jmsg(jcr, M_INFO, 0,
453         _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
454         dev->Slot, dev->drive_index);
455
456    Dmsg2(100, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
457         dev->Slot, dev->drive_index);
458
459    save_dev = dcr->dev;
460    dcr->dev = dev;
461    save_slot = dcr->VolCatInfo.Slot;
462    dcr->VolCatInfo.Slot = dev->Slot;
463    changer_cmd = edit_device_codes(dcr, changer_cmd, 
464                 dcr->device->changer_command, "unload");
465    dev->close();
466    Dmsg2(200, "close dev=%s reserve=%d\n", dev->print_name(), 
467       dev->num_reserved());
468    Dmsg1(100, "Run program=%s\n", changer_cmd);
469    int stat = run_program_full_output(changer_cmd, timeout, results.addr());
470    dcr->VolCatInfo.Slot = save_slot;
471    dcr->dev = save_dev;
472    if (stat != 0) {
473       berrno be;
474       be.set_errno(stat);
475       Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
476               dev->Slot, dev->drive_index, be.bstrerror());
477
478       Dmsg3(100, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
479               dev->Slot, dev->drive_index, be.bstrerror());
480       ok = false;
481       dev->Slot = -1;          /* unknown */
482    } else {
483       dev->Slot = 0;           /* nothing loaded */
484       Dmsg0(100, "Slot unloaded\n");
485    }
486    free_volume(dev);               /* Free any volume associated with this drive */
487    dev->clear_unload();
488    unlock_changer(dcr);
489    dev->dunlock();
490    free_pool_memory(changer_cmd);
491    return ok;
492 }
493
494
495
496 /*
497  * List the Volumes that are in the autoloader possibly
498  *   with their barcodes.
499  *   We assume that it is always the Console that is calling us.
500  */
501 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)  
502 {
503    DEVICE *dev = dcr->dev;
504    uint32_t timeout = dcr->device->max_changer_wait;
505    POOLMEM *changer;
506    BPIPE *bpipe;
507    int len = sizeof_pool_memory(dir->msg) - 1;
508    bool ok = false;
509    int stat;
510
511    if (!dev->is_autochanger() || !dcr->device->changer_name ||
512        !dcr->device->changer_command) {
513       if (strcmp(cmd, "drives") == 0) {
514          dir->fsend("drives=1\n");
515       }
516       dir->fsend(_("3993 Device %s not an autochanger device.\n"),
517          dev->print_name());
518       return false;
519    }
520
521    if (strcmp(cmd, "drives") == 0) {
522       AUTOCHANGER *changer_res = dcr->device->changer_res;
523       int drives = 1;
524       if (changer_res) {
525          drives = changer_res->device->size();
526       }
527       dir->fsend("drives=%d\n", drives);
528       Dmsg1(100, "drives=%d\n", drives);
529       return true;
530    }
531
532    changer = get_pool_memory(PM_FNAME);
533    lock_changer(dcr);
534    /* Now issue the command */
535    changer = edit_device_codes(dcr, changer, 
536                  dcr->device->changer_command, cmd);
537    dir->fsend(_("3306 Issuing autochanger \"%s\" command.\n"), cmd);
538    bpipe = open_bpipe(changer, timeout, "r");
539    if (!bpipe) {
540       dir->fsend(_("3996 Open bpipe failed.\n"));
541       goto bail_out;
542    }
543    if (strcmp(cmd, "list") == 0) {
544       /* Get output from changer */
545       while (fgets(dir->msg, len, bpipe->rfd)) {
546          dir->msglen = strlen(dir->msg);
547          Dmsg1(100, "<stored: %s\n", dir->msg);
548          bnet_send(dir);
549       }
550    } else if (strcmp(cmd, "slots") == 0 ) {
551       char buf[100], *p;
552       /* For slots command, read a single line */
553       buf[0] = 0;
554       fgets(buf, sizeof(buf)-1, bpipe->rfd);
555       buf[sizeof(buf)-1] = 0;
556       /* Strip any leading space in front of # of slots */
557       for (p=buf; B_ISSPACE(*p); p++)
558         { }
559       dir->fsend("slots=%s", p);
560       Dmsg1(100, "<stored: %s", dir->msg);
561    } 
562                  
563    stat = close_bpipe(bpipe);
564    if (stat != 0) {
565       berrno be;
566       be.set_errno(stat);
567       dir->fsend(_("Autochanger error: ERR=%s\n"), be.bstrerror());
568    }
569    bnet_sig(dir, BNET_EOD);
570    ok = true;
571
572 bail_out:
573    unlock_changer(dcr);
574    free_pool_memory(changer);
575    return true;
576 }
577
578
579 /*
580  * Edit codes into ChangerCommand
581  *  %% = %
582  *  %a = archive device name
583  *  %c = changer device name
584  *  %d = changer drive index
585  *  %f = Client's name
586  *  %j = Job name
587  *  %o = command
588  *  %s = Slot base 0
589  *  %S = Slot base 1
590  *  %v = Volume name
591  *
592  *
593  *  omsg = edited output message
594  *  imsg = input string containing edit codes (%x)
595  *  cmd = command string (load, unload, ...)
596  *
597  */
598 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
599 {
600    const char *p;
601    const char *str;
602    char add[20];
603
604    *omsg = 0;
605    Dmsg1(1800, "edit_device_codes: %s\n", imsg);
606    for (p=imsg; *p; p++) {
607       if (*p == '%') {
608          switch (*++p) {
609          case '%':
610             str = "%";
611             break;
612          case 'a':
613             str = dcr->dev->archive_name();
614             break;
615          case 'c':
616             str = NPRT(dcr->device->changer_name);
617             break;
618          case 'd':
619             sprintf(add, "%d", dcr->dev->drive_index);
620             str = add;
621             break;
622          case 'o':
623             str = NPRT(cmd);
624             break;
625          case 's':
626             sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
627             str = add;
628             break;
629          case 'S':
630             sprintf(add, "%d", dcr->VolCatInfo.Slot);
631             str = add;
632             break;
633          case 'j':                    /* Job name */
634             str = dcr->jcr->Job;
635             break;
636          case 'v':
637             str = NPRT(dcr->VolumeName);
638             break;
639          case 'f':
640             str = NPRT(dcr->jcr->client_name);
641             break;
642
643          default:
644             add[0] = '%';
645             add[1] = *p;
646             add[2] = 0;
647             str = add;
648             break;
649          }
650       } else {
651          add[0] = *p;
652          add[1] = 0;
653          str = add;
654       }
655       Dmsg1(1900, "add_str %s\n", str);
656       pm_strcat(&omsg, (char *)str);
657       Dmsg1(1800, "omsg=%s\n", omsg);
658    }
659    Dmsg1(800, "omsg=%s\n", omsg);
660    return omsg;
661 }