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