3 * Routines for handling the autochanger.
5 * Kern Sibbald, August MMII
10 Copyright (C) 2002-2006 Kern Sibbald
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 version 2 as amended with additional clauses defined in the
15 file LICENSE in the main source directory.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 the file LICENSE for additional details.
24 #include "bacula.h" /* pull in global headers */
25 #include "stored.h" /* pull in Storage Deamon headers */
27 /* Forward referenced functions */
28 static void lock_changer(DCR *dcr);
29 static void unlock_changer(DCR *dcr);
30 static bool unload_other_drive(DCR *dcr, int slot);
32 /* Init all the autochanger resources found */
33 bool init_autochangers()
37 /* Ensure that the media_type for each device is the same */
38 foreach_res(changer, R_AUTOCHANGER) {
40 foreach_alist(device, changer->device) {
42 * If the device does not have a changer name or changer command
43 * defined, used the one from the Autochanger resource
45 if (!device->changer_name && changer->changer_name) {
46 device->changer_name = bstrdup(changer->changer_name);
48 if (!device->changer_command && changer->changer_command) {
49 device->changer_command = bstrdup(changer->changer_command);
51 if (!device->changer_name) {
52 Jmsg(NULL, M_ERROR, 0,
53 _("No Changer Name given for device %s. Cannot continue.\n"),
57 if (!device->changer_command) {
58 Jmsg(NULL, M_ERROR, 0,
59 _("No Changer Command given for device %s. Cannot continue.\n"),
65 if (media_type == NULL) {
66 media_type = device->media_type; /* get Media Type of first device */
69 /* Ensure that other devices Media Types are the same */
70 if (strcmp(media_type, device->media_type) != 0) {
71 Jmsg(NULL, M_ERROR, 0,
72 _("Media Type not the same for all devices in changer %s. Cannot continue.\n"),
85 * Called here to do an autoload using the autochanger, if
86 * configured, and if a Slot has been defined for this Volume.
87 * On success this routine loads the indicated tape, but the
88 * label is not read, so it must be verified.
90 * Note if dir is not NULL, it is the console requesting the
91 * autoload for labeling, so we respond directly to the
94 * Returns: 1 on success
95 * 0 on failure (no changer available)
96 * -1 on error on autochanger
98 int autoload_device(DCR *dcr, int writing, BSOCK *dir)
101 DEVICE *dev = dcr->dev;
103 int drive = dev->drive_index;
104 int rtn_stat = -1; /* error status */
107 if (!dev->is_autochanger()) {
108 Dmsg0(200, "== NOT AUTOCHANGER ==\n");
111 slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
113 * Handle autoloaders here. If we cannot autoload it, we
114 * will return 0 so that the sysop will be asked to load it.
116 if (writing && slot <= 0) {
118 return 0; /* For user, bail out right now */
120 if (dir_find_next_appendable_volume(dcr)) {
121 slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
126 Dmsg1(400, "Want changer slot=%d\n", slot);
128 changer = get_pool_memory(PM_FNAME);
130 Jmsg(jcr, M_INFO, 0, _("Invalid slot=%d defined, cannot autoload Volume.\n"), slot);
132 } else if (!dcr->device->changer_name) {
133 Jmsg(jcr, M_INFO, 0, _("No \"Changer Device\" given cannot autoload Volume.\n"));
135 } else if (!dcr->device->changer_command) {
136 Jmsg(jcr, M_INFO, 0, _("No \"Changer Command\" given cannot autoload Volume.\n"));
139 /* Attempt to load the Volume */
141 uint32_t timeout = dcr->device->max_changer_wait;
144 loaded = get_autochanger_loaded_slot(dcr);
146 if (loaded != slot) {
148 /* Unload anything in our drive */
149 if (!unload_autochanger(dcr, loaded)) {
153 /* Make sure desired slot is unloaded */
154 if (!unload_other_drive(dcr, slot)) {
159 * Load the desired cassette
162 Dmsg1(100, "Doing changer load slot %d\n", slot);
164 _("3304 Issuing autochanger \"load slot %d, drive %d\" command.\n"),
166 dcr->VolCatInfo.Slot = slot; /* slot to be loaded */
167 changer = edit_device_codes(dcr, changer,
168 dcr->device->changer_command, "load");
170 Dmsg1(200, "Run program=%s\n", changer);
171 status = run_program(changer, timeout, NULL);
173 Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"),
175 Dmsg2(100, "load slot %d, drive %d, status is OK.\n", slot, drive);
176 dev->Slot = slot; /* set currently loaded slot */
179 be.set_errno(status);
180 Dmsg3(100, "load slot %d, drive %d, bad stats=%s.\n", slot, drive,
182 Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": ERR=%s.\n"),
183 slot, drive, be.strerror());
184 rtn_stat = -1; /* hard error */
185 dev->Slot = -1; /* mark unknown */
187 Dmsg2(100, "load slot %d status=%d\n", slot, status);
190 status = 0; /* we got what we want */
191 dev->Slot = slot; /* set currently loaded slot */
193 Dmsg1(100, "After changer, status=%d\n", status);
194 if (status == 0) { /* did we succeed? */
195 rtn_stat = 1; /* tape loaded by changer */
198 free_pool_memory(changer);
202 free_pool_memory(changer);
208 * Returns: -1 if error from changer command
210 * Note, this is safe to do without releasing the drive
211 * since it does not attempt load/unload a slot.
213 int get_autochanger_loaded_slot(DCR *dcr)
216 DEVICE *dev = dcr->dev;
217 POOLMEM *changer, *results;
219 uint32_t timeout = dcr->device->max_changer_wait;
220 int drive = dcr->dev->drive_index;
222 if (!dev->is_autochanger()) {
225 if (!dcr->device->changer_command) {
226 Jmsg(jcr, M_FATAL, 0, _("3992 Missing Changer command.\n"));
233 results = get_pool_memory(PM_MESSAGE);
234 changer = get_pool_memory(PM_FNAME);
237 /* Find out what is loaded, zero means device is unloaded */
239 Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded? drive %d\" command.\n"),
241 changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded");
243 Dmsg1(100, "Run program=%s\n", changer);
244 status = run_program(changer, timeout, results);
245 Dmsg3(100, "run_prog: %s stat=%d result=%s\n", changer, status, results);
247 loaded = str_to_int32(results);
249 Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result is Slot %d.\n"),
253 Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result: nothing loaded.\n"),
255 dev->Slot = -1; /* unknown */
259 be.set_errno(status);
260 Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded? drive %d\" command: ERR=%s.\n"),
261 drive, be.strerror());
262 loaded = -1; /* force unload */
265 free_pool_memory(changer);
266 free_pool_memory(results);
270 static void lock_changer(DCR *dcr)
272 AUTOCHANGER *changer_res = dcr->device->changer_res;
274 Dmsg1(200, "Locking changer %s\n", changer_res->hdr.name);
275 P(changer_res->changer_mutex); /* Lock changer script */
279 static void unlock_changer(DCR *dcr)
281 AUTOCHANGER *changer_res = dcr->device->changer_res;
283 Dmsg1(200, "Unlocking changer %s\n", changer_res->hdr.name);
284 V(changer_res->changer_mutex); /* Unlock changer script */
289 * Unload the volume, if any, in this drive
290 * On entry: loaded == 0 -- nothing to do
291 * loaded < 0 -- check if anything to do
292 * loaded > 0 -- load slot == loaded
294 bool unload_autochanger(DCR *dcr, int loaded)
296 DEVICE *dev = dcr->dev;
299 uint32_t timeout = dcr->device->max_changer_wait;
306 if (!dev->is_autochanger() || !dcr->device->changer_name ||
307 !dcr->device->changer_command) {
312 loaded = get_autochanger_loaded_slot(dcr);
316 POOLMEM *changer = get_pool_memory(PM_FNAME);
319 _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
320 loaded, dev->drive_index);
321 slot = dcr->VolCatInfo.Slot;
322 dcr->VolCatInfo.Slot = loaded;
323 changer = edit_device_codes(dcr, changer,
324 dcr->device->changer_command, "unload");
326 Dmsg1(100, "Run program=%s\n", changer);
327 int stat = run_program(changer, timeout, NULL);
328 dcr->VolCatInfo.Slot = slot;
332 Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
333 slot, dev->drive_index, be.strerror());
336 dev->Slot = -1; /* unknown */
338 free_pool_memory(changer);
345 * Unload the slot if mounted in a different drive
347 static bool unload_other_drive(DCR *dcr, int slot)
353 uint32_t timeout = dcr->device->max_changer_wait;
355 AUTOCHANGER *changer = dcr->dev->device->changer_res;
363 if (changer->device->size() == 1) {
367 foreach_alist(device, changer->device) {
368 if (device->dev && device->dev->Slot == slot) {
378 /* The Volume we want is on another device. */
379 if (dev->is_busy()) {
380 Dmsg4(100, "Vol %s for dev=%s in use dev=%s slot=%d\n",
381 dcr->VolumeName, dcr->dev->print_name(),
382 dev->print_name(), slot);
384 for (int i=0; i < 3; i++) {
385 if (dev->is_busy()) {
386 wait_for_device(dcr->jcr, first);
393 if (dev->is_busy()) {
394 Jmsg(jcr, M_WARNING, 0, _("Volume \"%s\" is in use by device %s\n"),
395 dcr->VolumeName, dev->print_name());
396 Dmsg4(100, "Vol %s for dev=%s is busy dev=%s slot=%d\n",
397 dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), slot);
398 Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->reserved_device);
403 POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
406 _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
407 slot, dev->drive_index);
409 Dmsg2(100, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
410 slot, dev->drive_index);
414 save_slot = dcr->VolCatInfo.Slot;
415 dcr->VolCatInfo.Slot = slot;
416 changer_cmd = edit_device_codes(dcr, changer_cmd,
417 dcr->device->changer_command, "unload");
419 Dmsg2(200, "close dev=%s reserve=%d\n", dev->print_name(),
420 dev->reserved_device);
421 Dmsg1(100, "Run program=%s\n", changer_cmd);
422 int stat = run_program(changer_cmd, timeout, NULL);
423 dcr->VolCatInfo.Slot = save_slot;
428 Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
429 slot, dev->drive_index, be.strerror());
431 Dmsg3(100, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
432 slot, dev->drive_index, be.strerror());
435 dev->Slot = -1; /* nothing loaded */
436 Dmsg0(100, "Slot unloaded\n");
440 free_pool_memory(changer_cmd);
447 * List the Volumes that are in the autoloader possibly
448 * with their barcodes.
449 * We assume that it is always the Console that is calling us.
451 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)
453 DEVICE *dev = dcr->dev;
454 uint32_t timeout = dcr->device->max_changer_wait;
457 int len = sizeof_pool_memory(dir->msg) - 1;
461 if (!dev->is_autochanger() || !dcr->device->changer_name ||
462 !dcr->device->changer_command) {
463 if (strcmp(cmd, "drives") == 0) {
464 bnet_fsend(dir, "drives=1\n");
466 bnet_fsend(dir, _("3993 Device %s not an autochanger device.\n"),
471 if (strcmp(cmd, "drives") == 0) {
472 AUTOCHANGER *changer_res = dcr->device->changer_res;
475 drives = changer_res->device->size();
477 bnet_fsend(dir, "drives=%d\n", drives);
478 Dmsg1(100, "drives=%d\n", drives);
482 changer = get_pool_memory(PM_FNAME);
484 /* Now issue the command */
485 changer = edit_device_codes(dcr, changer,
486 dcr->device->changer_command, cmd);
487 bnet_fsend(dir, _("3306 Issuing autochanger \"%s\" command.\n"), cmd);
488 bpipe = open_bpipe(changer, timeout, "r");
490 bnet_fsend(dir, _("3996 Open bpipe failed.\n"));
493 if (strcmp(cmd, "list") == 0) {
494 /* Get output from changer */
495 while (fgets(dir->msg, len, bpipe->rfd)) {
496 dir->msglen = strlen(dir->msg);
497 Dmsg1(100, "<stored: %s\n", dir->msg);
500 } else if (strcmp(cmd, "slots") == 0 ) {
502 /* For slots command, read a single line */
504 fgets(buf, sizeof(buf)-1, bpipe->rfd);
505 buf[sizeof(buf)-1] = 0;
506 /* Strip any leading space in front of # of slots */
507 for (p=buf; B_ISSPACE(*p); p++)
509 bnet_fsend(dir, "slots=%s", p);
510 Dmsg1(100, "<stored: %s", dir->msg);
513 stat = close_bpipe(bpipe);
517 bnet_fsend(dir, _("Autochanger error: ERR=%s\n"), be.strerror());
519 bnet_sig(dir, BNET_EOD);
524 free_pool_memory(changer);
530 * Edit codes into ChangerCommand
532 * %a = archive device name
533 * %c = changer device name
534 * %d = changer drive index
543 * omsg = edited output message
544 * imsg = input string containing edit codes (%x)
545 * cmd = command string (load, unload, ...)
548 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
555 Dmsg1(1800, "edit_device_codes: %s\n", imsg);
556 for (p=imsg; *p; p++) {
563 str = dcr->dev->archive_name();
566 str = NPRT(dcr->device->changer_name);
569 sprintf(add, "%d", dcr->dev->drive_index);
576 sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
580 sprintf(add, "%d", dcr->VolCatInfo.Slot);
583 case 'j': /* Job name */
587 str = NPRT(dcr->VolumeName);
590 str = NPRT(dcr->jcr->client_name);
605 Dmsg1(1900, "add_str %s\n", str);
606 pm_strcat(&omsg, (char *)str);
607 Dmsg1(1800, "omsg=%s\n", omsg);
609 Dmsg1(800, "omsg=%s\n", omsg);