2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2010 Free Software Foundation Europe e.V.
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
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.
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
23 Bacula® is a registered trademark of Kern Sibbald.
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.
30 * Routines for handling the autochanger.
32 * Kern Sibbald, August MMII
36 #include "bacula.h" /* pull in global headers */
37 #include "stored.h" /* pull in Storage Deamon headers */
39 /* Forward referenced functions */
40 static void lock_changer(DCR *dcr);
41 static void unlock_changer(DCR *dcr);
42 static bool unload_other_drive(DCR *dcr, int slot);
44 /* Init all the autochanger resources found */
45 bool init_autochangers()
49 /* Ensure that the media_type for each device is the same */
50 foreach_res(changer, R_AUTOCHANGER) {
52 foreach_alist(device, changer->device) {
54 * If the device does not have a changer name or changer command
55 * defined, used the one from the Autochanger resource
57 if (!device->changer_name && changer->changer_name) {
58 device->changer_name = bstrdup(changer->changer_name);
60 if (!device->changer_command && changer->changer_command) {
61 device->changer_command = bstrdup(changer->changer_command);
63 if (!device->changer_name) {
64 Jmsg(NULL, M_ERROR, 0,
65 _("No Changer Name given for device %s. Cannot continue.\n"),
69 if (!device->changer_command) {
70 Jmsg(NULL, M_ERROR, 0,
71 _("No Changer Command given for device %s. Cannot continue.\n"),
77 if (media_type == NULL) {
78 media_type = device->media_type; /* get Media Type of first device */
81 /* Ensure that other devices Media Types are the same */
82 if (strcmp(media_type, device->media_type) != 0) {
83 Jmsg(NULL, M_ERROR, 0,
84 _("Media Type not the same for all devices in changer %s. Cannot continue.\n"),
97 * Called here to do an autoload using the autochanger, if
98 * configured, and if a Slot has been defined for this Volume.
99 * On success this routine loads the indicated tape, but the
100 * label is not read, so it must be verified.
102 * Note if dir is not NULL, it is the console requesting the
103 * autoload for labeling, so we respond directly to the
106 * Returns: 1 on success
107 * 0 on failure (no changer available)
108 * -1 on error on autochanger
110 int autoload_device(DCR *dcr, int writing, BSOCK *dir)
113 DEVICE * volatile dev = dcr->dev;
115 int drive = dev->drive_index;
116 int rtn_stat = -1; /* error status */
119 if (!dev->is_autochanger()) {
120 Dmsg1(100, "Device %s is not an autochanger\n", dev->print_name());
124 /* An empty ChangerCommand => virtual disk autochanger */
125 if (dcr->device->changer_command && dcr->device->changer_command[0] == 0) {
126 Dmsg0(100, "ChangerCommand=0, virtual disk changer\n");
127 return 1; /* nothing to load */
130 slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
131 Dmsg3(100, "autoload: slot=%d InChgr=%d Vol=%s\n", dcr->VolCatInfo.Slot,
132 dcr->VolCatInfo.InChanger, dcr->getVolCatName());
134 * Handle autoloaders here. If we cannot autoload it, we
135 * will return 0 so that the sysop will be asked to load it.
137 if (writing && slot <= 0) {
139 return 0; /* For user, bail out right now */
141 /* ***FIXME*** this really should not be here */
142 if (dir_find_next_appendable_volume(dcr)) {
143 slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
148 Dmsg1(400, "Want changer slot=%d\n", slot);
150 changer = get_pool_memory(PM_FNAME);
152 Jmsg(jcr, M_INFO, 0, _("Invalid slot=%d defined in catalog for Volume \"%s\" "
153 "on %s. Manual load may be required.\n"), slot, dcr->getVolCatName(),
156 } else if (!dcr->device->changer_name) {
157 Jmsg(jcr, M_INFO, 0, _("No \"Changer Device\" for %s. Manual load of Volume may be required.\n"),
160 } else if (!dcr->device->changer_command) {
161 Jmsg(jcr, M_INFO, 0, _("No \"Changer Command\" for %s. Manual load of Volume may be requird.\n"),
165 /* Attempt to load the Volume */
167 uint32_t timeout = dcr->device->max_changer_wait;
170 loaded = get_autochanger_loaded_slot(dcr);
172 if (loaded != slot) {
173 POOL_MEM results(PM_MESSAGE);
175 /* Unload anything in our drive */
176 if (!unload_autochanger(dcr, loaded)) {
180 /* Make sure desired slot is unloaded */
181 if (!unload_other_drive(dcr, slot)) {
186 * Load the desired cassette
189 Dmsg2(100, "Doing changer load slot %d %s\n", slot, dev->print_name());
191 _("3304 Issuing autochanger \"load slot %d, drive %d\" command.\n"),
193 dcr->VolCatInfo.Slot = slot; /* slot to be loaded */
194 changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "load");
196 Dmsg1(200, "Run program=%s\n", changer);
197 status = run_program_full_output(changer, timeout, results.addr());
199 Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"),
201 Dmsg2(100, "load slot %d, drive %d, status is OK.\n", slot, drive);
202 dev->set_slot(slot); /* set currently loaded slot */
205 be.set_errno(status);
206 Dmsg3(100, "load slot %d, drive %d, bad stats=%s.\n", slot, drive,
208 Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": "
209 "ERR=%s.\nResults=%s\n"),
210 slot, drive, be.bstrerror(), results.c_str());
211 rtn_stat = -1; /* hard error */
212 dev->set_slot(-1); /* mark unknown */
214 Dmsg2(100, "load slot %d status=%d\n", slot, status);
217 status = 0; /* we got what we want */
218 dev->set_slot(slot); /* set currently loaded slot */
220 Dmsg1(100, "After changer, status=%d\n", status);
221 if (status == 0) { /* did we succeed? */
222 rtn_stat = 1; /* tape loaded by changer */
225 free_pool_memory(changer);
229 free_pool_memory(changer);
235 * Returns: -1 if error from changer command
237 * Note, this is safe to do without releasing the drive
238 * since it does not attempt load/unload a slot.
240 int get_autochanger_loaded_slot(DCR *dcr)
243 DEVICE *dev = dcr->dev;
245 uint32_t timeout = dcr->device->max_changer_wait;
246 int drive = dcr->dev->drive_index;
247 POOL_MEM results(PM_MESSAGE);
250 if (!dev->is_autochanger()) {
253 if (!dcr->device->changer_command) {
254 // Jmsg(jcr, M_FATAL, 0, _("3992 Missing Changer command.\n"));
257 if (dev->get_slot() > 0) {
258 return dev->get_slot();
261 /* Virtual disk autochanger */
262 if (dcr->device->changer_command[0] == 0) {
266 /* Find out what is loaded, zero means device is unloaded */
267 changer = get_pool_memory(PM_FNAME);
269 Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded? drive %d\" command.\n"),
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());
276 loaded = str_to_int32(results.c_str());
278 Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result is Slot %d.\n"),
280 dev->set_slot(loaded);
282 Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result: nothing loaded.\n"),
284 dev->clear_slot(); /* unknown */
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 */
294 free_pool_memory(changer);
298 static void lock_changer(DCR *dcr)
300 AUTOCHANGER *changer_res = dcr->device->changer_res;
302 Dmsg1(200, "Locking changer %s\n", changer_res->hdr.name);
303 P(changer_res->changer_mutex); /* Lock changer script */
307 static void unlock_changer(DCR *dcr)
309 AUTOCHANGER *changer_res = dcr->device->changer_res;
311 Dmsg1(200, "Unlocking changer %s\n", changer_res->hdr.name);
312 V(changer_res->changer_mutex); /* Unlock changer script */
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
322 bool unload_autochanger(DCR *dcr, int loaded)
324 DEVICE *dev = dcr->dev;
327 uint32_t timeout = dcr->device->max_changer_wait;
334 if (!dev->is_autochanger() || !dcr->device->changer_name ||
335 !dcr->device->changer_command) {
339 /* Virtual disk autochanger */
340 if (dcr->device->changer_command[0] == 0) {
346 loaded = get_autochanger_loaded_slot(dcr);
350 POOL_MEM results(PM_MESSAGE);
351 POOLMEM *changer = get_pool_memory(PM_FNAME);
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");
361 Dmsg1(100, "Run program=%s\n", changer);
362 int stat = run_program_full_output(changer, timeout, results.addr());
363 dcr->VolCatInfo.Slot = slot;
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());
371 dev->clear_slot(); /* unknown */
373 dev->set_slot(0); /* nothing loaded */
377 free_volume(dev); /* Free any volume associated with this drive */
378 free_pool_memory(changer);
387 * Unload the slot if mounted in a different drive
389 static bool unload_other_drive(DCR *dcr, int slot)
393 AUTOCHANGER *changer = dcr->dev->device->changer_res;
395 int retries = 0; /* wait for device retries */
400 if (changer->device->size() == 1) {
404 foreach_alist(device, changer->device) {
405 if (device->dev && device->dev->get_slot() == slot) {
415 /* The Volume we want is on another device. */
416 if (dev->is_busy()) {
417 Dmsg4(100, "Vol %s for dev=%s in use dev=%s slot=%d\n",
418 dcr->VolumeName, dcr->dev->print_name(),
419 dev->print_name(), slot);
421 for (int i=0; i < 3; i++) {
422 if (dev->is_busy()) {
423 wait_for_device(dcr->jcr, retries);
428 if (dev->is_busy()) {
429 Jmsg(dcr->jcr, M_WARNING, 0, _("Volume \"%s\" wanted on %s is in use by device %s\n"),
430 dcr->VolumeName, dcr->dev->print_name(), dev->print_name());
431 Dmsg4(100, "Vol %s for dev=%s is busy dev=%s slot=%d\n",
432 dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), dev->get_slot());
433 Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->num_reserved());
437 return unload_dev(dcr, dev);
441 * Unconditionally unload a specified drive
443 bool unload_dev(DCR *dcr, DEVICE *dev)
447 uint32_t timeout = dcr->device->max_changer_wait;
448 AUTOCHANGER *changer = dcr->dev->device->changer_res;
456 save_dev = dcr->dev; /* save dcr device */
457 dcr->dev = dev; /* temporarily point dcr at other device */
459 if (dev->get_slot() <= 0 && get_autochanger_loaded_slot(dcr) <= 0) {
463 save_slot = dcr->VolCatInfo.Slot;
464 dcr->VolCatInfo.Slot = dev->get_slot();
468 POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
469 POOL_MEM results(PM_MESSAGE);
472 _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
473 dev->get_slot(), dev->drive_index);
475 Dmsg2(100, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
476 dev->get_slot(), dev->drive_index);
478 changer_cmd = edit_device_codes(dcr, changer_cmd,
479 dcr->device->changer_command, "unload");
481 Dmsg2(200, "close dev=%s reserve=%d\n", dev->print_name(),
482 dev->num_reserved());
483 Dmsg1(100, "Run program=%s\n", changer_cmd);
484 int stat = run_program_full_output(changer_cmd, timeout, results.addr());
485 dcr->VolCatInfo.Slot = save_slot;
490 Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
491 dev->get_slot(), dev->drive_index, be.bstrerror());
493 Dmsg3(100, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
494 dev->get_slot(), dev->drive_index, be.bstrerror());
496 dev->clear_slot(); /* unknown */
498 Dmsg2(100, "Slot %d unloaded %s\n", dev->get_slot(), dev->print_name());
499 dev->set_slot(0); /* nothing loaded */
508 free_volume(dev); /* Free any volume associated with this drive */
509 free_pool_memory(changer_cmd);
516 * List the Volumes that are in the autoloader possibly
517 * with their barcodes.
518 * We assume that it is always the Console that is calling us.
520 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)
522 DEVICE *dev = dcr->dev;
523 uint32_t timeout = dcr->device->max_changer_wait;
526 int len = sizeof_pool_memory(dir->msg) - 1;
530 if (!dev->is_autochanger() || !dcr->device->changer_name ||
531 !dcr->device->changer_command) {
532 if (strcmp(cmd, "drives") == 0) {
533 dir->fsend("drives=1\n");
535 dir->fsend(_("3993 Device %s not an autochanger device.\n"),
540 if (strcmp(cmd, "drives") == 0) {
541 AUTOCHANGER *changer_res = dcr->device->changer_res;
544 drives = changer_res->device->size();
546 dir->fsend("drives=%d\n", drives);
547 Dmsg1(100, "drives=%d\n", drives);
551 changer = get_pool_memory(PM_FNAME);
553 /* Now issue the command */
554 changer = edit_device_codes(dcr, changer,
555 dcr->device->changer_command, cmd);
556 dir->fsend(_("3306 Issuing autochanger \"%s\" command.\n"), cmd);
557 bpipe = open_bpipe(changer, timeout, "r");
559 dir->fsend(_("3996 Open bpipe failed.\n"));
562 if (bstrcmp(cmd, "list") || bstrcmp(cmd, "listall")) {
563 /* Get output from changer */
564 while (fgets(dir->msg, len, bpipe->rfd)) {
565 dir->msglen = strlen(dir->msg);
566 Dmsg1(100, "<stored: %s\n", dir->msg);
569 } else if (strcmp(cmd, "slots") == 0 ) {
571 /* For slots command, read a single line */
573 fgets(buf, sizeof(buf)-1, bpipe->rfd);
574 buf[sizeof(buf)-1] = 0;
575 /* Strip any leading space in front of # of slots */
576 for (p=buf; B_ISSPACE(*p); p++)
578 dir->fsend("slots=%s", p);
579 Dmsg1(100, "<stored: %s", dir->msg);
582 stat = close_bpipe(bpipe);
586 dir->fsend(_("Autochanger error: ERR=%s\n"), be.bstrerror());
588 bnet_sig(dir, BNET_EOD);
593 free_pool_memory(changer);
599 * Edit codes into ChangerCommand
601 * %a = archive device name
602 * %c = changer device name
603 * %d = changer drive index
612 * omsg = edited output message
613 * imsg = input string containing edit codes (%x)
614 * cmd = command string (load, unload, ...)
617 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
624 Dmsg1(1800, "edit_device_codes: %s\n", imsg);
625 for (p=imsg; *p; p++) {
632 str = dcr->dev->archive_name();
635 str = NPRT(dcr->device->changer_name);
638 sprintf(add, "%d", dcr->dev->drive_index);
645 sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
649 sprintf(add, "%d", dcr->VolCatInfo.Slot);
652 case 'j': /* Job name */
656 str = NPRT(dcr->VolumeName);
659 str = NPRT(dcr->jcr->client_name);
674 Dmsg1(1900, "add_str %s\n", str);
675 pm_strcat(&omsg, (char *)str);
676 Dmsg1(1800, "omsg=%s\n", omsg);
678 Dmsg1(800, "omsg=%s\n", omsg);