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