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