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