]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/autochanger.c
Tweak volume swapping code so it works.
[bacula/bacula] / bacula / src / stored / autochanger.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2008 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *  Routines for handling the autochanger.
31  *
32  *   Kern Sibbald, August MMII
33  *                            
34  *   Version $Id$
35  */
36
37 #include "bacula.h"                   /* pull in global headers */
38 #include "stored.h"                   /* pull in Storage Deamon headers */
39
40 /* Forward referenced functions */
41 static void lock_changer(DCR *dcr);
42 static void unlock_changer(DCR *dcr);
43 static bool unload_other_drive(DCR *dcr, int slot);
44
45 /* Init all the autochanger resources found */
46 bool init_autochangers()
47 {
48    bool OK = true;
49    AUTOCHANGER *changer;
50    /* Ensure that the media_type for each device is the same */
51    foreach_res(changer, R_AUTOCHANGER) {
52       DEVRES *device;
53       foreach_alist(device, changer->device) {
54          /*
55           * If the device does not have a changer name or changer command
56           *   defined, used the one from the Autochanger resource 
57           */
58          if (!device->changer_name && changer->changer_name) {
59             device->changer_name = bstrdup(changer->changer_name);
60          }
61          if (!device->changer_command && changer->changer_command) {
62             device->changer_command = bstrdup(changer->changer_command);
63          }
64          if (!device->changer_name) {
65             Jmsg(NULL, M_ERROR, 0, 
66                _("No Changer Name given for device %s. Cannot continue.\n"),
67                device->hdr.name);
68             OK = false;
69          }   
70          if (!device->changer_command) {
71             Jmsg(NULL, M_ERROR, 0, 
72                _("No Changer Command given for device %s. Cannot continue.\n"),
73                device->hdr.name);
74             OK = false;
75          }   
76
77 #ifdef xxx_needed
78          if (media_type == NULL) {
79             media_type = device->media_type;     /* get Media Type of first device */
80             continue;
81          }     
82          /* Ensure that other devices Media Types are the same */
83          if (strcmp(media_type, device->media_type) != 0) {
84             Jmsg(NULL, M_ERROR, 0, 
85                _("Media Type not the same for all devices in changer %s. Cannot continue.\n"),
86                changer->hdr.name);
87             OK = false;
88             continue;
89          }
90 #endif
91       }
92    }
93    return OK;
94 }
95
96
97 /*
98  * Called here to do an autoload using the autochanger, if
99  *  configured, and if a Slot has been defined for this Volume.
100  *  On success this routine loads the indicated tape, but the
101  *  label is not read, so it must be verified.
102  *
103  *  Note if dir is not NULL, it is the console requesting the
104  *   autoload for labeling, so we respond directly to the
105  *   dir bsock.
106  *
107  *  Returns: 1 on success
108  *           0 on failure (no changer available)
109  *          -1 on error on autochanger
110  */
111 int autoload_device(DCR *dcr, int writing, BSOCK *dir)
112 {
113    JCR *jcr = dcr->jcr;
114    DEVICE * volatile dev = dcr->dev;
115    int slot;
116    int drive = dev->drive_index;
117    int rtn_stat = -1;                 /* error status */
118    POOLMEM *changer;
119
120    if (!dev->is_autochanger()) {
121       Dmsg1(200, "Device %s is not an autochanger\n", dev->print_name());
122       return 0;
123    }
124
125    /* An empty ChangerCommand => virtual disk autochanger */
126    if (dcr->device->changer_command && dcr->device->changer_command[0] == 0) {
127       return 1;                       /* nothing to load */
128    }
129
130    slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
131    /*
132     * Handle autoloaders here.  If we cannot autoload it, we
133     *  will return 0 so that the sysop will be asked to load it.
134     */
135    if (writing && slot <= 0) {
136       if (dir) {
137          return 0;                    /* For user, bail out right now */
138       }
139       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    bool found = false;
384    AUTOCHANGER *changer = dcr->dev->device->changer_res;
385    DEVRES *device;
386    int retries = 0;                /* wait for device retries */
387
388    if (!changer) {
389       return false;
390    }
391    if (changer->device->size() == 1) {
392       return true;
393    }
394
395    foreach_alist(device, changer->device) {
396       if (device->dev && device->dev->Slot == slot) {
397          found = true;
398          dev = device->dev;
399          break;
400       }
401    }
402    if (!found) {
403       return true;
404    }
405
406    /* The Volume we want is on another device. */
407    if (dev->is_busy()) {
408       Dmsg4(100, "Vol %s for dev=%s in use dev=%s slot=%d\n",
409            dcr->VolumeName, dcr->dev->print_name(),
410            dev->print_name(), slot);
411    }   
412    for (int i=0; i < 3; i++) {
413       if (dev->is_busy()) {
414          wait_for_device(dcr->jcr, retries);
415          continue;
416       }
417       break;
418    }
419    if (dev->is_busy()) {
420       Jmsg(dcr->jcr, M_WARNING, 0, _("Volume \"%s\" is in use by device %s\n"),
421            dcr->VolumeName, dev->print_name());
422       Dmsg4(100, "Vol %s for dev=%s is busy dev=%s slot=%d\n",
423            dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), dev->Slot);
424       Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->reserved_device);
425       return false;
426    }
427    return unload_dev(dcr, dev);
428 }
429
430 bool unload_dev(DCR *dcr, DEVICE *dev)
431 {
432    JCR *jcr = dcr->jcr;
433    bool ok = true;
434    uint32_t timeout = dcr->device->max_changer_wait;
435    AUTOCHANGER *changer = dcr->dev->device->changer_res;
436    DEVICE *save_dev;
437    int save_slot;
438
439    if (!changer || dev->Slot <= 0) {
440       return false;
441    }
442    dev->dlock();
443
444    POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
445    POOL_MEM results(PM_MESSAGE);
446    lock_changer(dcr);
447    Jmsg(jcr, M_INFO, 0,
448         _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
449         dev->Slot, dev->drive_index);
450
451    Dmsg2(100, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
452         dev->Slot, dev->drive_index);
453
454    save_dev = dcr->dev;
455    dcr->dev = dev;
456    save_slot = dcr->VolCatInfo.Slot;
457    dcr->VolCatInfo.Slot = dev->Slot;
458    changer_cmd = edit_device_codes(dcr, changer_cmd, 
459                 dcr->device->changer_command, "unload");
460    dev->close();
461    Dmsg2(200, "close dev=%s reserve=%d\n", dev->print_name(), 
462       dev->reserved_device);
463    Dmsg1(100, "Run program=%s\n", changer_cmd);
464    int stat = run_program_full_output(changer_cmd, timeout, results.addr());
465    dcr->VolCatInfo.Slot = save_slot;
466    dcr->dev = save_dev;
467    if (stat != 0) {
468       berrno be;
469       be.set_errno(stat);
470       Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
471               dev->Slot, dev->drive_index, be.bstrerror());
472
473       Dmsg3(100, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
474               dev->Slot, dev->drive_index, be.bstrerror());
475       ok = false;
476       dev->Slot = -1;          /* unknown */
477    } else {
478       dev->Slot = 0;           /* nothing loaded */
479       Dmsg0(100, "Slot unloaded\n");
480    }
481    free_volume(dev);               /* Free any volume associated with this drive */
482    unlock_changer(dcr);
483    dev->dunlock();
484    free_pool_memory(changer_cmd);
485    return ok;
486 }
487
488
489
490 /*
491  * List the Volumes that are in the autoloader possibly
492  *   with their barcodes.
493  *   We assume that it is always the Console that is calling us.
494  */
495 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)  
496 {
497    DEVICE *dev = dcr->dev;
498    uint32_t timeout = dcr->device->max_changer_wait;
499    POOLMEM *changer;
500    BPIPE *bpipe;
501    int len = sizeof_pool_memory(dir->msg) - 1;
502    bool ok = false;
503    int stat;
504
505    if (!dev->is_autochanger() || !dcr->device->changer_name ||
506        !dcr->device->changer_command) {
507       if (strcmp(cmd, "drives") == 0) {
508          dir->fsend("drives=1\n");
509       }
510       dir->fsend(_("3993 Device %s not an autochanger device.\n"),
511          dev->print_name());
512       return false;
513    }
514
515    if (strcmp(cmd, "drives") == 0) {
516       AUTOCHANGER *changer_res = dcr->device->changer_res;
517       int drives = 1;
518       if (changer_res) {
519          drives = changer_res->device->size();
520       }
521       dir->fsend("drives=%d\n", drives);
522       Dmsg1(100, "drives=%d\n", drives);
523       return true;
524    }
525
526    changer = get_pool_memory(PM_FNAME);
527    lock_changer(dcr);
528    /* Now issue the command */
529    changer = edit_device_codes(dcr, changer, 
530                  dcr->device->changer_command, cmd);
531    dir->fsend(_("3306 Issuing autochanger \"%s\" command.\n"), cmd);
532    bpipe = open_bpipe(changer, timeout, "r");
533    if (!bpipe) {
534       dir->fsend(_("3996 Open bpipe failed.\n"));
535       goto bail_out;
536    }
537    if (strcmp(cmd, "list") == 0) {
538       /* Get output from changer */
539       while (fgets(dir->msg, len, bpipe->rfd)) {
540          dir->msglen = strlen(dir->msg);
541          Dmsg1(100, "<stored: %s\n", dir->msg);
542          bnet_send(dir);
543       }
544    } else if (strcmp(cmd, "slots") == 0 ) {
545       char buf[100], *p;
546       /* For slots command, read a single line */
547       buf[0] = 0;
548       fgets(buf, sizeof(buf)-1, bpipe->rfd);
549       buf[sizeof(buf)-1] = 0;
550       /* Strip any leading space in front of # of slots */
551       for (p=buf; B_ISSPACE(*p); p++)
552         { }
553       dir->fsend("slots=%s", p);
554       Dmsg1(100, "<stored: %s", dir->msg);
555    } 
556                  
557    stat = close_bpipe(bpipe);
558    if (stat != 0) {
559       berrno be;
560       be.set_errno(stat);
561       dir->fsend(_("Autochanger error: ERR=%s\n"), be.bstrerror());
562    }
563    bnet_sig(dir, BNET_EOD);
564    ok = true;
565
566 bail_out:
567    unlock_changer(dcr);
568    free_pool_memory(changer);
569    return true;
570 }
571
572
573 /*
574  * Edit codes into ChangerCommand
575  *  %% = %
576  *  %a = archive device name
577  *  %c = changer device name
578  *  %d = changer drive index
579  *  %f = Client's name
580  *  %j = Job name
581  *  %o = command
582  *  %s = Slot base 0
583  *  %S = Slot base 1
584  *  %v = Volume name
585  *
586  *
587  *  omsg = edited output message
588  *  imsg = input string containing edit codes (%x)
589  *  cmd = command string (load, unload, ...)
590  *
591  */
592 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
593 {
594    const char *p;
595    const char *str;
596    char add[20];
597
598    *omsg = 0;
599    Dmsg1(1800, "edit_device_codes: %s\n", imsg);
600    for (p=imsg; *p; p++) {
601       if (*p == '%') {
602          switch (*++p) {
603          case '%':
604             str = "%";
605             break;
606          case 'a':
607             str = dcr->dev->archive_name();
608             break;
609          case 'c':
610             str = NPRT(dcr->device->changer_name);
611             break;
612          case 'd':
613             sprintf(add, "%d", dcr->dev->drive_index);
614             str = add;
615             break;
616          case 'o':
617             str = NPRT(cmd);
618             break;
619          case 's':
620             sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
621             str = add;
622             break;
623          case 'S':
624             sprintf(add, "%d", dcr->VolCatInfo.Slot);
625             str = add;
626             break;
627          case 'j':                    /* Job name */
628             str = dcr->jcr->Job;
629             break;
630          case 'v':
631             str = NPRT(dcr->VolumeName);
632             break;
633          case 'f':
634             str = NPRT(dcr->jcr->client_name);
635             break;
636
637          default:
638             add[0] = '%';
639             add[1] = *p;
640             add[2] = 0;
641             str = add;
642             break;
643          }
644       } else {
645          add[0] = *p;
646          add[1] = 0;
647          str = add;
648       }
649       Dmsg1(1900, "add_str %s\n", str);
650       pm_strcat(&omsg, (char *)str);
651       Dmsg1(1800, "omsg=%s\n", omsg);
652    }
653    Dmsg1(800, "omsg=%s\n", omsg);
654    return omsg;
655 }