]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/autochanger.c
kes Modify autochanger locking to attempt to avoid race
[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       unlock_changer(dcr);
376
377       free_volume(dev);            /* Free any volume associated with this drive */
378       free_pool_memory(changer);
379    }
380    return ok;
381 }
382
383 /*
384  * Unload the slot if mounted in a different drive
385  */
386 static bool unload_other_drive(DCR *dcr, int slot)
387 {
388    DEVICE *dev = NULL;
389    bool found = false;
390    AUTOCHANGER *changer = dcr->dev->device->changer_res;
391    DEVRES *device;
392    int retries = 0;                /* wait for device retries */
393
394    if (!changer) {
395       return false;
396    }
397    if (changer->device->size() == 1) {
398       return true;
399    }
400
401    foreach_alist(device, changer->device) {
402       if (device->dev && device->dev->Slot == slot) {
403          found = true;
404          dev = device->dev;
405          break;
406       }
407    }
408    if (!found) {
409       return true;
410    }
411
412    /* The Volume we want is on another device. */
413    if (dev->is_busy()) {
414       Dmsg4(100, "Vol %s for dev=%s in use dev=%s slot=%d\n",
415            dcr->VolumeName, dcr->dev->print_name(),
416            dev->print_name(), slot);
417    }   
418    for (int i=0; i < 3; i++) {
419       if (dev->is_busy()) {
420          wait_for_device(dcr->jcr, retries);
421          continue;
422       }
423       break;
424    }
425    if (dev->is_busy()) {
426       Jmsg(dcr->jcr, M_WARNING, 0, _("Volume \"%s\" is in use by device %s\n"),
427            dcr->VolumeName, dev->print_name());
428       Dmsg4(100, "Vol %s for dev=%s is busy dev=%s slot=%d\n",
429            dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), dev->Slot);
430       Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->num_reserved());
431       return false;
432    }
433    return unload_dev(dcr, dev);
434 }
435
436 bool unload_dev(DCR *dcr, DEVICE *dev)
437 {
438    JCR *jcr = dcr->jcr;
439    bool ok = true;
440    uint32_t timeout = dcr->device->max_changer_wait;
441    AUTOCHANGER *changer = dcr->dev->device->changer_res;
442    DEVICE *save_dev;
443    int save_slot;
444
445    if (!changer || dev->Slot <= 0) {
446       return false;
447    }
448    dev->dlock();
449
450    POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
451    POOL_MEM results(PM_MESSAGE);
452    lock_changer(dcr);
453    Jmsg(jcr, M_INFO, 0,
454         _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
455         dev->Slot, dev->drive_index);
456
457    Dmsg2(100, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
458         dev->Slot, dev->drive_index);
459
460    save_dev = dcr->dev;
461    dcr->dev = dev;
462    save_slot = dcr->VolCatInfo.Slot;
463    dcr->VolCatInfo.Slot = dev->Slot;
464    changer_cmd = edit_device_codes(dcr, changer_cmd, 
465                 dcr->device->changer_command, "unload");
466    dev->close();
467    Dmsg2(200, "close dev=%s reserve=%d\n", dev->print_name(), 
468       dev->num_reserved());
469    Dmsg1(100, "Run program=%s\n", changer_cmd);
470    int stat = run_program_full_output(changer_cmd, timeout, results.addr());
471    dcr->VolCatInfo.Slot = save_slot;
472    dcr->dev = save_dev;
473    if (stat != 0) {
474       berrno be;
475       be.set_errno(stat);
476       Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
477               dev->Slot, dev->drive_index, be.bstrerror());
478
479       Dmsg3(100, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
480               dev->Slot, dev->drive_index, be.bstrerror());
481       ok = false;
482       dev->Slot = -1;          /* unknown */
483    } else {
484       dev->Slot = 0;           /* nothing loaded */
485       Dmsg0(100, "Slot unloaded\n");
486    }
487    dev->clear_unload();
488    unlock_changer(dcr);
489
490    dev->dunlock();
491
492    free_volume(dev);               /* Free any volume associated with this drive */
493    free_pool_memory(changer_cmd);
494    return ok;
495 }
496
497
498
499 /*
500  * List the Volumes that are in the autoloader possibly
501  *   with their barcodes.
502  *   We assume that it is always the Console that is calling us.
503  */
504 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)  
505 {
506    DEVICE *dev = dcr->dev;
507    uint32_t timeout = dcr->device->max_changer_wait;
508    POOLMEM *changer;
509    BPIPE *bpipe;
510    int len = sizeof_pool_memory(dir->msg) - 1;
511    bool ok = false;
512    int stat;
513
514    if (!dev->is_autochanger() || !dcr->device->changer_name ||
515        !dcr->device->changer_command) {
516       if (strcmp(cmd, "drives") == 0) {
517          dir->fsend("drives=1\n");
518       }
519       dir->fsend(_("3993 Device %s not an autochanger device.\n"),
520          dev->print_name());
521       return false;
522    }
523
524    if (strcmp(cmd, "drives") == 0) {
525       AUTOCHANGER *changer_res = dcr->device->changer_res;
526       int drives = 1;
527       if (changer_res) {
528          drives = changer_res->device->size();
529       }
530       dir->fsend("drives=%d\n", drives);
531       Dmsg1(100, "drives=%d\n", drives);
532       return true;
533    }
534
535    changer = get_pool_memory(PM_FNAME);
536    lock_changer(dcr);
537    /* Now issue the command */
538    changer = edit_device_codes(dcr, changer, 
539                  dcr->device->changer_command, cmd);
540    dir->fsend(_("3306 Issuing autochanger \"%s\" command.\n"), cmd);
541    bpipe = open_bpipe(changer, timeout, "r");
542    if (!bpipe) {
543       dir->fsend(_("3996 Open bpipe failed.\n"));
544       goto bail_out;
545    }
546    if (strcmp(cmd, "list") == 0) {
547       /* Get output from changer */
548       while (fgets(dir->msg, len, bpipe->rfd)) {
549          dir->msglen = strlen(dir->msg);
550          Dmsg1(100, "<stored: %s\n", dir->msg);
551          bnet_send(dir);
552       }
553    } else if (strcmp(cmd, "slots") == 0 ) {
554       char buf[100], *p;
555       /* For slots command, read a single line */
556       buf[0] = 0;
557       fgets(buf, sizeof(buf)-1, bpipe->rfd);
558       buf[sizeof(buf)-1] = 0;
559       /* Strip any leading space in front of # of slots */
560       for (p=buf; B_ISSPACE(*p); p++)
561         { }
562       dir->fsend("slots=%s", p);
563       Dmsg1(100, "<stored: %s", dir->msg);
564    } 
565                  
566    stat = close_bpipe(bpipe);
567    if (stat != 0) {
568       berrno be;
569       be.set_errno(stat);
570       dir->fsend(_("Autochanger error: ERR=%s\n"), be.bstrerror());
571    }
572    bnet_sig(dir, BNET_EOD);
573    ok = true;
574
575 bail_out:
576    unlock_changer(dcr);
577    free_pool_memory(changer);
578    return true;
579 }
580
581
582 /*
583  * Edit codes into ChangerCommand
584  *  %% = %
585  *  %a = archive device name
586  *  %c = changer device name
587  *  %d = changer drive index
588  *  %f = Client's name
589  *  %j = Job name
590  *  %o = command
591  *  %s = Slot base 0
592  *  %S = Slot base 1
593  *  %v = Volume name
594  *
595  *
596  *  omsg = edited output message
597  *  imsg = input string containing edit codes (%x)
598  *  cmd = command string (load, unload, ...)
599  *
600  */
601 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
602 {
603    const char *p;
604    const char *str;
605    char add[20];
606
607    *omsg = 0;
608    Dmsg1(1800, "edit_device_codes: %s\n", imsg);
609    for (p=imsg; *p; p++) {
610       if (*p == '%') {
611          switch (*++p) {
612          case '%':
613             str = "%";
614             break;
615          case 'a':
616             str = dcr->dev->archive_name();
617             break;
618          case 'c':
619             str = NPRT(dcr->device->changer_name);
620             break;
621          case 'd':
622             sprintf(add, "%d", dcr->dev->drive_index);
623             str = add;
624             break;
625          case 'o':
626             str = NPRT(cmd);
627             break;
628          case 's':
629             sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
630             str = add;
631             break;
632          case 'S':
633             sprintf(add, "%d", dcr->VolCatInfo.Slot);
634             str = add;
635             break;
636          case 'j':                    /* Job name */
637             str = dcr->jcr->Job;
638             break;
639          case 'v':
640             str = NPRT(dcr->VolumeName);
641             break;
642          case 'f':
643             str = NPRT(dcr->jcr->client_name);
644             break;
645
646          default:
647             add[0] = '%';
648             add[1] = *p;
649             add[2] = 0;
650             str = add;
651             break;
652          }
653       } else {
654          add[0] = *p;
655          add[1] = 0;
656          str = add;
657       }
658       Dmsg1(1900, "add_str %s\n", str);
659       pm_strcat(&omsg, (char *)str);
660       Dmsg1(1800, "omsg=%s\n", omsg);
661    }
662    Dmsg1(800, "omsg=%s\n", omsg);
663    return omsg;
664 }