]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/autochanger.c
11Apr07
[bacula/bacula] / bacula / src / stored / autochanger.c
1 /*
2  *
3  *  Routines for handling the autochanger.
4  *
5  *   Kern Sibbald, August MMII
6  *                            
7  *   Version $Id$
8  */
9 /*
10    Bacula® - The Network Backup Solution
11
12    Copyright (C) 2002-2006 Free Software Foundation Europe e.V.
13
14    The main author of Bacula is Kern Sibbald, with contributions from
15    many others, a complete list can be found in the file AUTHORS.
16    This program is Free Software; you can redistribute it and/or
17    modify it under the terms of version two of the GNU General Public
18    License as published by the Free Software Foundation plus additions
19    that are listed in the file LICENSE.
20
21    This program is distributed in the hope that it will be useful, but
22    WITHOUT ANY WARRANTY; without even the implied warranty of
23    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24    General Public License for more details.
25
26    You should have received a copy of the GNU General Public License
27    along with this program; if not, write to the Free Software
28    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
29    02110-1301, USA.
30
31    Bacula® is a registered trademark of John Walker.
32    The licensor of Bacula is the Free Software Foundation Europe
33    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
34    Switzerland, email:ftf@fsfeurope.org.
35 */
36
37 #include "bacula.h"                   /* pull in global headers */
38 #include "stored.h"                   /* pull in Storage Deamon headers */
39
40 /* Forward referenced functions */
41 static void lock_changer(DCR *dcr);
42 static void unlock_changer(DCR *dcr);
43 static bool unload_other_drive(DCR *dcr, int slot);
44
45 /* Init all the autochanger resources found */
46 bool init_autochangers()
47 {
48    bool OK = true;
49    AUTOCHANGER *changer;
50    /* Ensure that the media_type for each device is the same */
51    foreach_res(changer, R_AUTOCHANGER) {
52       DEVRES *device;
53       foreach_alist(device, changer->device) {
54          /*
55           * If the device does not have a changer name or changer command
56           *   defined, used the one from the Autochanger resource 
57           */
58          if (!device->changer_name && changer->changer_name) {
59             device->changer_name = bstrdup(changer->changer_name);
60          }
61          if (!device->changer_command && changer->changer_command) {
62             device->changer_command = bstrdup(changer->changer_command);
63          }
64          if (!device->changer_name) {
65             Jmsg(NULL, M_ERROR, 0, 
66                _("No Changer Name given for device %s. Cannot continue.\n"),
67                device->hdr.name);
68             OK = false;
69          }   
70          if (!device->changer_command) {
71             Jmsg(NULL, M_ERROR, 0, 
72                _("No Changer Command given for device %s. Cannot continue.\n"),
73                device->hdr.name);
74             OK = false;
75          }   
76
77 #ifdef xxx_needed
78          if (media_type == NULL) {
79             media_type = device->media_type;     /* get Media Type of first device */
80             continue;
81          }     
82          /* Ensure that other devices Media Types are the same */
83          if (strcmp(media_type, device->media_type) != 0) {
84             Jmsg(NULL, M_ERROR, 0, 
85                _("Media Type not the same for all devices in changer %s. Cannot continue.\n"),
86                changer->hdr.name);
87             OK = false;
88             continue;
89          }
90 #endif
91       }
92    }
93    return OK;
94 }
95
96
97 /*
98  * Called here to do an autoload using the autochanger, if
99  *  configured, and if a Slot has been defined for this Volume.
100  *  On success this routine loads the indicated tape, but the
101  *  label is not read, so it must be verified.
102  *
103  *  Note if dir is not NULL, it is the console requesting the
104  *   autoload for labeling, so we respond directly to the
105  *   dir bsock.
106  *
107  *  Returns: 1 on success
108  *           0 on failure (no changer available)
109  *          -1 on error on autochanger
110  */
111 int autoload_device(DCR *dcr, int writing, BSOCK *dir)
112 {
113    JCR *jcr = dcr->jcr;
114    DEVICE *dev = dcr->dev;
115    int slot;
116    int drive = dev->drive_index;
117    int rtn_stat = -1;                 /* error status */
118    POOLMEM *changer;
119
120    if (!dev->is_autochanger()) {
121       Dmsg1(200, "Device %s is not an autochanger\n", dev->print_name());
122       return 0;
123    }
124
125    /* An empty ChangerCommand => virtual disk autochanger */
126    if (dcr->device->changer_command && dcr->device->changer_command[0] == 0) {
127       return 1;                       /* nothing to load */
128    }
129
130    slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
131    /*
132     * Handle autoloaders here.  If we cannot autoload it, we
133     *  will return 0 so that the sysop will be asked to load it.
134     */
135    if (writing && slot <= 0) {
136       if (dir) {
137          return 0;                    /* For user, bail out right now */
138       }
139       if (dir_find_next_appendable_volume(dcr)) {
140          slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
141       } else {
142          slot = 0;
143       }
144    }
145    Dmsg1(400, "Want changer slot=%d\n", slot);
146
147    changer = get_pool_memory(PM_FNAME);
148    if (slot <= 0) {
149       Jmsg(jcr, M_INFO, 0, _("Invalid slot=%d defined in catalog. Manual load my be required.\n"), slot);
150       rtn_stat = 0;
151    } else if (!dcr->device->changer_name) {
152       Jmsg(jcr, M_INFO, 0, _("No \"Changer Device\" manual load of Volume may be required.\n"));
153       rtn_stat = 0;
154   } else if (!dcr->device->changer_command) {
155       Jmsg(jcr, M_INFO, 0, _("No \"Changer Command\" manual load of Volume may be requird.\n"));
156       rtn_stat = 0;
157   } else {
158       /* Attempt to load the Volume */
159
160       uint32_t timeout = dcr->device->max_changer_wait;
161       int loaded, status;
162
163       loaded = get_autochanger_loaded_slot(dcr);
164
165       if (loaded != slot) {
166          POOL_MEM results(PM_MESSAGE);
167
168          /* Unload anything in our drive */
169          if (!unload_autochanger(dcr, loaded)) {
170             goto bail_out;
171          }
172             
173          /* Make sure desired slot is unloaded */
174          if (!unload_other_drive(dcr, slot)) {
175             goto bail_out;
176          }
177
178          /*
179           * Load the desired cassette
180           */
181          lock_changer(dcr);
182          Dmsg1(100, "Doing changer load slot %d\n", slot);
183          Jmsg(jcr, M_INFO, 0,
184               _("3304 Issuing autochanger \"load slot %d, drive %d\" command.\n"),
185               slot, drive);
186          dcr->VolCatInfo.Slot = slot;    /* slot to be loaded */
187          changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "load");
188          dev->close();
189          Dmsg1(200, "Run program=%s\n", changer);
190          status = run_program_full_output(changer, timeout, results.c_str());
191          if (status == 0) {
192             Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"),
193                     slot, drive);
194             Dmsg2(100, "load slot %d, drive %d, status is OK.\n", slot, drive);
195             dev->Slot = slot;         /* set currently loaded slot */
196          } else {
197             berrno be;
198             be.set_errno(status);
199             Dmsg3(100, "load slot %d, drive %d, bad stats=%s.\n", slot, drive,
200                be.strerror());
201             Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": "
202                  "ERR=%s.\nResults=%s\n"),
203                     slot, drive, be.strerror(), results.c_str());
204             rtn_stat = -1;            /* hard error */
205             dev->Slot = -1;           /* mark unknown */
206          }
207          Dmsg2(100, "load slot %d status=%d\n", slot, status);
208          unlock_changer(dcr);
209       } else {
210          status = 0;                  /* we got what we want */
211          dev->Slot = slot;            /* set currently loaded slot */
212       }
213       Dmsg1(100, "After changer, status=%d\n", status);
214       if (status == 0) {              /* did we succeed? */
215          rtn_stat = 1;                /* tape loaded by changer */
216       }
217    }
218    free_pool_memory(changer);
219    return rtn_stat;
220
221 bail_out:
222    free_pool_memory(changer);
223    return -1;
224
225 }
226
227 /*
228  * Returns: -1 if error from changer command
229  *          slot otherwise
230  *  Note, this is safe to do without releasing the drive
231  *   since it does not attempt load/unload a slot.
232  */
233 int get_autochanger_loaded_slot(DCR *dcr)
234 {
235    JCR *jcr = dcr->jcr;
236    DEVICE *dev = dcr->dev;
237    int status, loaded;
238    uint32_t timeout = dcr->device->max_changer_wait;
239    int drive = dcr->dev->drive_index;
240    POOL_MEM results(PM_MESSAGE);
241    POOLMEM *changer;
242
243    if (!dev->is_autochanger()) {
244       return -1;
245    }
246    if (!dcr->device->changer_command) {
247       Jmsg(jcr, M_FATAL, 0, _("3992 Missing Changer command.\n"));
248       return -1;
249    }
250    if (dev->Slot > 0) {
251       return dev->Slot;
252    }
253    /* Virtual disk autochanger */
254    if (dcr->device->changer_command[0] ==0) {
255       return 1;
256    }
257
258    /* Find out what is loaded, zero means device is unloaded */
259    changer = get_pool_memory(PM_FNAME);
260    lock_changer(dcr);
261    Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded? drive %d\" command.\n"),
262         drive);
263    changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded");
264    *results.c_str() = 0;
265    Dmsg1(100, "Run program=%s\n", changer);
266    status = run_program_full_output(changer, timeout, results.c_str());
267    Dmsg3(100, "run_prog: %s stat=%d result=%s", changer, status, results.c_str());
268    if (status == 0) {
269       loaded = str_to_int32(results.c_str());
270       if (loaded > 0) {
271          Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result is Slot %d.\n"),
272               drive, loaded);
273          dev->Slot = loaded;
274       } else {
275          Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result: nothing loaded.\n"),
276               drive);
277          dev->Slot = -1;    /* unknown */
278       }
279    } else {
280       berrno be;
281       be.set_errno(status);
282       Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded? drive %d\" command: "
283            "ERR=%s.\nResults=%s\n"), drive, be.strerror(), results.c_str());
284       loaded = -1;              /* force unload */
285    }
286    unlock_changer(dcr);
287    free_pool_memory(changer);
288    return loaded;
289 }
290
291 static void lock_changer(DCR *dcr)
292 {
293    AUTOCHANGER *changer_res = dcr->device->changer_res;
294    if (changer_res) {
295       Dmsg1(200, "Locking changer %s\n", changer_res->hdr.name);
296       P(changer_res->changer_mutex);  /* Lock changer script */
297    }
298 }
299
300 static void unlock_changer(DCR *dcr)
301 {
302    AUTOCHANGER *changer_res = dcr->device->changer_res;
303    if (changer_res) {
304       Dmsg1(200, "Unlocking changer %s\n", changer_res->hdr.name);
305       V(changer_res->changer_mutex);  /* Unlock changer script */
306    }
307 }
308
309 /*
310  * Unload the volume, if any, in this drive
311  *  On entry: loaded == 0 -- nothing to do
312  *            loaded  < 0 -- check if anything to do
313  *            loaded  > 0 -- load slot == loaded
314  */
315 bool unload_autochanger(DCR *dcr, int loaded)
316 {
317    DEVICE *dev = dcr->dev;
318    JCR *jcr = dcr->jcr;
319    int slot;
320    uint32_t timeout = dcr->device->max_changer_wait;
321    bool ok = true;
322
323    if (loaded == 0) {
324       return true;
325    }
326
327    if (!dev->is_autochanger() || !dcr->device->changer_name ||
328        !dcr->device->changer_command) {
329       return false;
330    }
331
332    if (loaded < 0) {
333       loaded = get_autochanger_loaded_slot(dcr);
334    }
335
336    if (loaded > 0) {
337       POOL_MEM results(PM_MESSAGE);
338       POOLMEM *changer = get_pool_memory(PM_FNAME);
339       lock_changer(dcr);
340       Jmsg(jcr, M_INFO, 0,
341            _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
342            loaded, dev->drive_index);
343       slot = dcr->VolCatInfo.Slot;
344       dcr->VolCatInfo.Slot = loaded;
345       changer = edit_device_codes(dcr, changer, 
346                    dcr->device->changer_command, "unload");
347       dev->close();
348       Dmsg1(100, "Run program=%s\n", changer);
349       *results.c_str() = 0;
350       int stat = run_program_full_output(changer, timeout, results.c_str());
351       dcr->VolCatInfo.Slot = slot;
352       if (stat != 0) {
353          berrno be;
354          be.set_errno(stat);
355          Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": "
356               "ERR=%s\nResults=%s\n"),
357                  loaded, dev->drive_index, be.strerror(), results.c_str());
358          ok = false;
359          dev->Slot = -1;           /* unknown */
360       } else {
361          dev->Slot = 0;            /* nothing loaded */
362       }
363       free_pool_memory(changer);
364       unlock_changer(dcr);
365    }
366    return ok;
367 }
368
369 /*
370  * Unload the slot if mounted in a different drive
371  */
372 static bool unload_other_drive(DCR *dcr, int slot)
373 {
374    DEVICE *dev = NULL;
375    DEVICE *save_dev;
376    JCR *jcr = dcr->jcr;
377    int save_slot;
378    uint32_t timeout = dcr->device->max_changer_wait;
379    bool ok = true;
380    AUTOCHANGER *changer = dcr->dev->device->changer_res;
381    DEVRES *device;
382    bool found = false;
383    bool first = true;
384
385    if (!changer) {
386       return false;
387    }
388    if (changer->device->size() == 1) {
389       return true;
390    }
391
392    foreach_alist(device, changer->device) {
393       if (device->dev && device->dev->Slot == slot) {
394          found = true;
395          dev = device->dev;
396          break;
397       }
398    }
399    if (!found) {
400       return true;
401    }
402
403    /* The Volume we want is on another device. */
404    if (dev->is_busy()) {
405       Dmsg4(100, "Vol %s for dev=%s in use dev=%s slot=%d\n",
406            dcr->VolumeName, dcr->dev->print_name(),
407            dev->print_name(), slot);
408    }   
409    for (int i=0; i < 3; i++) {
410       if (dev->is_busy()) {
411          wait_for_device(dcr->jcr, first);
412          first = false;
413          continue;
414       }
415       break;
416    }
417    dev->dlock();
418    if (dev->is_busy()) {
419       Jmsg(jcr, M_WARNING, 0, _("Volume \"%s\" is in use by device %s\n"),
420            dcr->VolumeName, dev->print_name());
421       Dmsg4(100, "Vol %s for dev=%s is busy dev=%s slot=%d\n",
422            dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), slot);
423       Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->reserved_device);
424       dev->dunlock();
425       return false;
426    }
427
428    POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
429    POOL_MEM results(PM_MESSAGE);
430    lock_changer(dcr);
431    Jmsg(jcr, M_INFO, 0,
432         _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
433         slot, dev->drive_index);
434
435    Dmsg2(100, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
436         slot, dev->drive_index);
437
438    save_dev = dcr->dev;
439    dcr->dev = dev;
440    save_slot = dcr->VolCatInfo.Slot;
441    dcr->VolCatInfo.Slot = slot;
442    changer_cmd = edit_device_codes(dcr, changer_cmd, 
443                 dcr->device->changer_command, "unload");
444    dev->close();
445    Dmsg2(200, "close dev=%s reserve=%d\n", dev->print_name(), 
446       dev->reserved_device);
447    Dmsg1(100, "Run program=%s\n", changer_cmd);
448    int stat = run_program_full_output(changer_cmd, timeout, results.c_str());
449    dcr->VolCatInfo.Slot = save_slot;
450    dcr->dev = save_dev;
451    if (stat != 0) {
452       berrno be;
453       be.set_errno(stat);
454       Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
455               slot, dev->drive_index, be.strerror());
456
457       Dmsg3(100, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
458               slot, dev->drive_index, be.strerror());
459       ok = false;
460       dev->Slot = -1;          /* unknown */
461    } else {
462       dev->Slot = 0;           /* nothing loaded */
463       Dmsg0(100, "Slot unloaded\n");
464    }
465    unlock_changer(dcr);
466    dev->dunlock();
467    free_pool_memory(changer_cmd);
468    return ok;
469 }
470
471
472
473 /*
474  * List the Volumes that are in the autoloader possibly
475  *   with their barcodes.
476  *   We assume that it is always the Console that is calling us.
477  */
478 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)  
479 {
480    DEVICE *dev = dcr->dev;
481    uint32_t timeout = dcr->device->max_changer_wait;
482    POOLMEM *changer;
483    BPIPE *bpipe;
484    int len = sizeof_pool_memory(dir->msg) - 1;
485    bool ok = false;
486    int stat;
487
488    if (!dev->is_autochanger() || !dcr->device->changer_name ||
489        !dcr->device->changer_command) {
490       if (strcmp(cmd, "drives") == 0) {
491          bnet_fsend(dir, "drives=1\n");
492       }
493       bnet_fsend(dir, _("3993 Device %s not an autochanger device.\n"),
494          dev->print_name());
495       return false;
496    }
497
498    if (strcmp(cmd, "drives") == 0) {
499       AUTOCHANGER *changer_res = dcr->device->changer_res;
500       int drives = 1;
501       if (changer_res) {
502          drives = changer_res->device->size();
503       }
504       bnet_fsend(dir, "drives=%d\n", drives);
505       Dmsg1(100, "drives=%d\n", drives);
506       return true;
507    }
508
509    changer = get_pool_memory(PM_FNAME);
510    lock_changer(dcr);
511    /* Now issue the command */
512    changer = edit_device_codes(dcr, changer, 
513                  dcr->device->changer_command, cmd);
514    bnet_fsend(dir, _("3306 Issuing autochanger \"%s\" command.\n"), cmd);
515    bpipe = open_bpipe(changer, timeout, "r");
516    if (!bpipe) {
517       bnet_fsend(dir, _("3996 Open bpipe failed.\n"));
518       goto bail_out;
519    }
520    if (strcmp(cmd, "list") == 0) {
521       /* Get output from changer */
522       while (fgets(dir->msg, len, bpipe->rfd)) {
523          dir->msglen = strlen(dir->msg);
524          Dmsg1(100, "<stored: %s\n", dir->msg);
525          bnet_send(dir);
526       }
527    } else if (strcmp(cmd, "slots") == 0 ) {
528       char buf[100], *p;
529       /* For slots command, read a single line */
530       buf[0] = 0;
531       fgets(buf, sizeof(buf)-1, bpipe->rfd);
532       buf[sizeof(buf)-1] = 0;
533       /* Strip any leading space in front of # of slots */
534       for (p=buf; B_ISSPACE(*p); p++)
535         { }
536       bnet_fsend(dir, "slots=%s", p);
537       Dmsg1(100, "<stored: %s", dir->msg);
538    } 
539                  
540    stat = close_bpipe(bpipe);
541    if (stat != 0) {
542       berrno be;
543       be.set_errno(stat);
544       bnet_fsend(dir, _("Autochanger error: ERR=%s\n"), be.strerror());
545    }
546    bnet_sig(dir, BNET_EOD);
547    ok = true;
548
549 bail_out:
550    unlock_changer(dcr);
551    free_pool_memory(changer);
552    return true;
553 }
554
555
556 /*
557  * Edit codes into ChangerCommand
558  *  %% = %
559  *  %a = archive device name
560  *  %c = changer device name
561  *  %d = changer drive index
562  *  %f = Client's name
563  *  %j = Job name
564  *  %o = command
565  *  %s = Slot base 0
566  *  %S = Slot base 1
567  *  %v = Volume name
568  *
569  *
570  *  omsg = edited output message
571  *  imsg = input string containing edit codes (%x)
572  *  cmd = command string (load, unload, ...)
573  *
574  */
575 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
576 {
577    const char *p;
578    const char *str;
579    char add[20];
580
581    *omsg = 0;
582    Dmsg1(1800, "edit_device_codes: %s\n", imsg);
583    for (p=imsg; *p; p++) {
584       if (*p == '%') {
585          switch (*++p) {
586          case '%':
587             str = "%";
588             break;
589          case 'a':
590             str = dcr->dev->archive_name();
591             break;
592          case 'c':
593             str = NPRT(dcr->device->changer_name);
594             break;
595          case 'd':
596             sprintf(add, "%d", dcr->dev->drive_index);
597             str = add;
598             break;
599          case 'o':
600             str = NPRT(cmd);
601             break;
602          case 's':
603             sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
604             str = add;
605             break;
606          case 'S':
607             sprintf(add, "%d", dcr->VolCatInfo.Slot);
608             str = add;
609             break;
610          case 'j':                    /* Job name */
611             str = dcr->jcr->Job;
612             break;
613          case 'v':
614             str = NPRT(dcr->VolumeName);
615             break;
616          case 'f':
617             str = NPRT(dcr->jcr->client_name);
618             break;
619
620          default:
621             add[0] = '%';
622             add[1] = *p;
623             add[2] = 0;
624             str = add;
625             break;
626          }
627       } else {
628          add[0] = *p;
629          add[1] = 0;
630          str = add;
631       }
632       Dmsg1(1900, "add_str %s\n", str);
633       pm_strcat(&omsg, (char *)str);
634       Dmsg1(1800, "omsg=%s\n", omsg);
635    }
636    Dmsg1(800, "omsg=%s\n", omsg);
637    return omsg;
638 }