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