2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2008 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
37 #include "bacula.h" /* pull in global headers */
38 #include "stored.h" /* pull in Storage Deamon headers */
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);
45 /* Init all the autochanger resources found */
46 bool init_autochangers()
50 /* Ensure that the media_type for each device is the same */
51 foreach_res(changer, R_AUTOCHANGER) {
53 foreach_alist(device, changer->device) {
55 * If the device does not have a changer name or changer command
56 * defined, used the one from the Autochanger resource
58 if (!device->changer_name && changer->changer_name) {
59 device->changer_name = bstrdup(changer->changer_name);
61 if (!device->changer_command && changer->changer_command) {
62 device->changer_command = bstrdup(changer->changer_command);
64 if (!device->changer_name) {
65 Jmsg(NULL, M_ERROR, 0,
66 _("No Changer Name given for device %s. Cannot continue.\n"),
70 if (!device->changer_command) {
71 Jmsg(NULL, M_ERROR, 0,
72 _("No Changer Command given for device %s. Cannot continue.\n"),
78 if (media_type == NULL) {
79 media_type = device->media_type; /* get Media Type of first device */
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"),
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.
103 * Note if dir is not NULL, it is the console requesting the
104 * autoload for labeling, so we respond directly to the
107 * Returns: 1 on success
108 * 0 on failure (no changer available)
109 * -1 on error on autochanger
111 int autoload_device(DCR *dcr, int writing, BSOCK *dir)
114 DEVICE * volatile dev = dcr->dev;
116 int drive = dev->drive_index;
117 int rtn_stat = -1; /* error status */
120 if (!dev->is_autochanger()) {
121 Dmsg1(100, "Device %s is not an autochanger\n", dev->print_name());
125 /* An empty ChangerCommand => virtual disk autochanger */
126 if (dcr->device->changer_command && dcr->device->changer_command[0] == 0) {
127 Dmsg0(100, "ChangerCommand=0, virtual disk changer\n");
128 return 1; /* nothing to load */
131 slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
132 Dmsg3(100, "autoload: slot=%d InChgr=%d Vol=%s\n", dcr->VolCatInfo.Slot,
133 dcr->VolCatInfo.InChanger, dcr->VolCatInfo.VolCatName);
135 * Handle autoloaders here. If we cannot autoload it, we
136 * will return 0 so that the sysop will be asked to load it.
138 if (writing && slot <= 0) {
140 return 0; /* For user, bail out right now */
142 /* ***FIXME*** this really should not be here */
143 if (dir_find_next_appendable_volume(dcr)) {
144 slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
149 Dmsg1(400, "Want changer slot=%d\n", slot);
151 changer = get_pool_memory(PM_FNAME);
153 Jmsg(jcr, M_INFO, 0, _("Invalid slot=%d defined in catalog for Volume \"%s\" "
154 "on %s. Manual load may be required.\n"), slot, dcr->VolCatInfo.VolCatName,
157 } else if (!dcr->device->changer_name) {
158 Jmsg(jcr, M_INFO, 0, _("No \"Changer Device\" for %s. Manual load of Volume may be required.\n"),
161 } else if (!dcr->device->changer_command) {
162 Jmsg(jcr, M_INFO, 0, _("No \"Changer Command\" for %s. Manual load of Volume may be requird.\n"),
166 /* Attempt to load the Volume */
168 uint32_t timeout = dcr->device->max_changer_wait;
171 loaded = get_autochanger_loaded_slot(dcr);
173 if (loaded != slot) {
174 POOL_MEM results(PM_MESSAGE);
176 /* Unload anything in our drive */
177 if (!unload_autochanger(dcr, loaded)) {
181 /* Make sure desired slot is unloaded */
182 if (!unload_other_drive(dcr, slot)) {
187 * Load the desired cassette
190 Dmsg2(100, "Doing changer load slot %d %s\n", slot, dev->print_name());
192 _("3304 Issuing autochanger \"load slot %d, drive %d\" command.\n"),
194 dcr->VolCatInfo.Slot = slot; /* slot to be loaded */
195 changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "load");
197 Dmsg1(200, "Run program=%s\n", changer);
198 status = run_program_full_output(changer, timeout, results.addr());
200 Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"),
202 Dmsg2(100, "load slot %d, drive %d, status is OK.\n", slot, drive);
203 dev->set_slot(slot); /* set currently loaded slot */
206 be.set_errno(status);
207 Dmsg3(100, "load slot %d, drive %d, bad stats=%s.\n", slot, drive,
209 Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": "
210 "ERR=%s.\nResults=%s\n"),
211 slot, drive, be.bstrerror(), results.c_str());
212 rtn_stat = -1; /* hard error */
213 dev->set_slot(-1); /* mark unknown */
215 Dmsg2(100, "load slot %d status=%d\n", slot, status);
218 status = 0; /* we got what we want */
219 dev->set_slot(slot); /* set currently loaded slot */
221 Dmsg1(100, "After changer, status=%d\n", status);
222 if (status == 0) { /* did we succeed? */
223 rtn_stat = 1; /* tape loaded by changer */
226 free_pool_memory(changer);
230 free_pool_memory(changer);
236 * Returns: -1 if error from changer command
238 * Note, this is safe to do without releasing the drive
239 * since it does not attempt load/unload a slot.
241 int get_autochanger_loaded_slot(DCR *dcr)
244 DEVICE *dev = dcr->dev;
246 uint32_t timeout = dcr->device->max_changer_wait;
247 int drive = dcr->dev->drive_index;
248 POOL_MEM results(PM_MESSAGE);
251 if (!dev->is_autochanger()) {
254 if (!dcr->device->changer_command) {
255 // Jmsg(jcr, M_FATAL, 0, _("3992 Missing Changer command.\n"));
258 if (dev->get_slot() > 0) {
259 return dev->get_slot();
262 /* Virtual disk autochanger */
263 if (dcr->device->changer_command[0] == 0) {
267 /* Find out what is loaded, zero means device is unloaded */
268 changer = get_pool_memory(PM_FNAME);
270 Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded? drive %d\" command.\n"),
272 changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded");
273 Dmsg1(100, "Run program=%s\n", changer);
274 status = run_program_full_output(changer, timeout, results.addr());
275 Dmsg3(100, "run_prog: %s stat=%d result=%s", changer, status, results.c_str());
277 loaded = str_to_int32(results.c_str());
279 Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result is Slot %d.\n"),
281 dev->set_slot(loaded);
283 Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result: nothing loaded.\n"),
285 dev->clear_slot(); /* unknown */
289 be.set_errno(status);
290 Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded? drive %d\" command: "
291 "ERR=%s.\nResults=%s\n"), drive, be.bstrerror(), results.c_str());
292 loaded = -1; /* force unload */
295 free_pool_memory(changer);
299 static void lock_changer(DCR *dcr)
301 AUTOCHANGER *changer_res = dcr->device->changer_res;
303 Dmsg1(200, "Locking changer %s\n", changer_res->hdr.name);
304 P(changer_res->changer_mutex); /* Lock changer script */
308 static void unlock_changer(DCR *dcr)
310 AUTOCHANGER *changer_res = dcr->device->changer_res;
312 Dmsg1(200, "Unlocking changer %s\n", changer_res->hdr.name);
313 V(changer_res->changer_mutex); /* Unlock changer script */
318 * Unload the volume, if any, in this drive
319 * On entry: loaded == 0 -- nothing to do
320 * loaded < 0 -- check if anything to do
321 * loaded > 0 -- load slot == loaded
323 bool unload_autochanger(DCR *dcr, int loaded)
325 DEVICE *dev = dcr->dev;
328 uint32_t timeout = dcr->device->max_changer_wait;
335 if (!dev->is_autochanger() || !dcr->device->changer_name ||
336 !dcr->device->changer_command) {
340 /* Virtual disk autochanger */
341 if (dcr->device->changer_command[0] == 0) {
347 loaded = get_autochanger_loaded_slot(dcr);
351 POOL_MEM results(PM_MESSAGE);
352 POOLMEM *changer = get_pool_memory(PM_FNAME);
355 _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
356 loaded, dev->drive_index);
357 slot = dcr->VolCatInfo.Slot;
358 dcr->VolCatInfo.Slot = loaded;
359 changer = edit_device_codes(dcr, changer,
360 dcr->device->changer_command, "unload");
362 Dmsg1(100, "Run program=%s\n", changer);
363 int stat = run_program_full_output(changer, timeout, results.addr());
364 dcr->VolCatInfo.Slot = slot;
368 Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": "
369 "ERR=%s\nResults=%s\n"),
370 loaded, dev->drive_index, be.bstrerror(), results.c_str());
372 dev->clear_slot(); /* unknown */
374 dev->set_slot(0); /* nothing loaded */
378 free_volume(dev); /* Free any volume associated with this drive */
379 free_pool_memory(changer);
388 * Unload the slot if mounted in a different drive
390 static bool unload_other_drive(DCR *dcr, int slot)
394 AUTOCHANGER *changer = dcr->dev->device->changer_res;
396 int retries = 0; /* wait for device retries */
401 if (changer->device->size() == 1) {
405 foreach_alist(device, changer->device) {
406 if (device->dev && device->dev->get_slot() == slot) {
416 /* The Volume we want is on another device. */
417 if (dev->is_busy()) {
418 Dmsg4(100, "Vol %s for dev=%s in use dev=%s slot=%d\n",
419 dcr->VolumeName, dcr->dev->print_name(),
420 dev->print_name(), slot);
422 for (int i=0; i < 3; i++) {
423 if (dev->is_busy()) {
424 wait_for_device(dcr->jcr, retries);
429 if (dev->is_busy()) {
430 Jmsg(dcr->jcr, M_WARNING, 0, _("Volume \"%s\" wanted on %s is in use by device %s\n"),
431 dcr->VolumeName, dcr->dev->print_name(), dev->print_name());
432 Dmsg4(100, "Vol %s for dev=%s is busy dev=%s slot=%d\n",
433 dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), dev->get_slot());
434 Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->num_reserved());
438 return unload_dev(dcr, dev);
442 * Unconditionally unload a specified drive
444 bool unload_dev(DCR *dcr, DEVICE *dev)
448 uint32_t timeout = dcr->device->max_changer_wait;
449 AUTOCHANGER *changer = dcr->dev->device->changer_res;
457 save_dev = dcr->dev; /* save dcr device */
458 dcr->dev = dev; /* temporarily point dcr at other device */
460 if (dev->get_slot() <= 0 && get_autochanger_loaded_slot(dcr) <= 0) {
464 save_slot = dcr->VolCatInfo.Slot;
465 dcr->VolCatInfo.Slot = dev->get_slot();
469 POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
470 POOL_MEM results(PM_MESSAGE);
473 _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
474 dev->get_slot(), dev->drive_index);
476 Dmsg2(100, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
477 dev->get_slot(), dev->drive_index);
479 changer_cmd = edit_device_codes(dcr, changer_cmd,
480 dcr->device->changer_command, "unload");
482 Dmsg2(200, "close dev=%s reserve=%d\n", dev->print_name(),
483 dev->num_reserved());
484 Dmsg1(100, "Run program=%s\n", changer_cmd);
485 int stat = run_program_full_output(changer_cmd, timeout, results.addr());
486 dcr->VolCatInfo.Slot = save_slot;
491 Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
492 dev->get_slot(), dev->drive_index, be.bstrerror());
494 Dmsg3(100, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
495 dev->get_slot(), dev->drive_index, be.bstrerror());
497 dev->clear_slot(); /* unknown */
499 Dmsg2(100, "Slot %d unloaded %s\n", dev->get_slot(), dev->print_name());
500 dev->set_slot(0); /* nothing loaded */
509 free_volume(dev); /* Free any volume associated with this drive */
510 free_pool_memory(changer_cmd);
517 * List the Volumes that are in the autoloader possibly
518 * with their barcodes.
519 * We assume that it is always the Console that is calling us.
521 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)
523 DEVICE *dev = dcr->dev;
524 uint32_t timeout = dcr->device->max_changer_wait;
527 int len = sizeof_pool_memory(dir->msg) - 1;
531 if (!dev->is_autochanger() || !dcr->device->changer_name ||
532 !dcr->device->changer_command) {
533 if (strcmp(cmd, "drives") == 0) {
534 dir->fsend("drives=1\n");
536 dir->fsend(_("3993 Device %s not an autochanger device.\n"),
541 if (strcmp(cmd, "drives") == 0) {
542 AUTOCHANGER *changer_res = dcr->device->changer_res;
545 drives = changer_res->device->size();
547 dir->fsend("drives=%d\n", drives);
548 Dmsg1(100, "drives=%d\n", drives);
552 changer = get_pool_memory(PM_FNAME);
554 /* Now issue the command */
555 changer = edit_device_codes(dcr, changer,
556 dcr->device->changer_command, cmd);
557 dir->fsend(_("3306 Issuing autochanger \"%s\" command.\n"), cmd);
558 bpipe = open_bpipe(changer, timeout, "r");
560 dir->fsend(_("3996 Open bpipe failed.\n"));
563 if (bstrcmp(cmd, "list") || bstrcmp(cmd, "listall")) {
564 /* Get output from changer */
565 while (fgets(dir->msg, len, bpipe->rfd)) {
566 dir->msglen = strlen(dir->msg);
567 Dmsg1(100, "<stored: %s\n", dir->msg);
570 } else if (strcmp(cmd, "slots") == 0 ) {
572 /* For slots command, read a single line */
574 fgets(buf, sizeof(buf)-1, bpipe->rfd);
575 buf[sizeof(buf)-1] = 0;
576 /* Strip any leading space in front of # of slots */
577 for (p=buf; B_ISSPACE(*p); p++)
579 dir->fsend("slots=%s", p);
580 Dmsg1(100, "<stored: %s", dir->msg);
583 stat = close_bpipe(bpipe);
587 dir->fsend(_("Autochanger error: ERR=%s\n"), be.bstrerror());
589 bnet_sig(dir, BNET_EOD);
594 free_pool_memory(changer);
600 * Edit codes into ChangerCommand
602 * %a = archive device name
603 * %c = changer device name
604 * %d = changer drive index
613 * omsg = edited output message
614 * imsg = input string containing edit codes (%x)
615 * cmd = command string (load, unload, ...)
618 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
625 Dmsg1(1800, "edit_device_codes: %s\n", imsg);
626 for (p=imsg; *p; p++) {
633 str = dcr->dev->archive_name();
636 str = NPRT(dcr->device->changer_name);
639 sprintf(add, "%d", dcr->dev->drive_index);
646 sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
650 sprintf(add, "%d", dcr->VolCatInfo.Slot);
653 case 'j': /* Job name */
657 str = NPRT(dcr->VolumeName);
660 str = NPRT(dcr->jcr->client_name);
675 Dmsg1(1900, "add_str %s\n", str);
676 pm_strcat(&omsg, (char *)str);
677 Dmsg1(1800, "omsg=%s\n", omsg);
679 Dmsg1(800, "omsg=%s\n", omsg);