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);
129 if (slot > 0 && dcr->device->changer_name && dcr->device->changer_command) {
130 uint32_t timeout = dcr->device->max_changer_wait;
133 loaded = get_autochanger_loaded_slot(dcr);
135 if (loaded != slot) {
137 /* Unload anything in our drive */
138 if (!unload_autochanger(dcr, loaded)) {
142 /* Make sure desired slot is unloaded */
143 if (!unload_other_drive(dcr, slot)) {
148 * Load the desired cassette
151 Dmsg1(100, "Doing changer load slot %d\n", slot);
153 _("3304 Issuing autochanger \"load slot %d, drive %d\" command.\n"),
155 dcr->VolCatInfo.Slot = slot; /* slot to be loaded */
156 changer = edit_device_codes(dcr, changer,
157 dcr->device->changer_command, "load");
159 Dmsg1(200, "Run program=%s\n", changer);
160 status = run_program(changer, timeout, NULL);
162 Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"),
164 Dmsg2(100, "load slot %d, drive %d, status is OK.\n", slot, drive);
165 dev->Slot = slot; /* set currently loaded slot */
168 be.set_errno(status);
169 Dmsg3(100, "load slot %d, drive %d, bad stats=%s.\n", slot, drive,
171 Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": ERR=%s.\n"),
172 slot, drive, be.strerror());
173 rtn_stat = -1; /* hard error */
175 Dmsg2(100, "load slot %d status=%d\n", slot, status);
178 status = 0; /* we got what we want */
179 dev->Slot = slot; /* set currently loaded slot */
181 Dmsg1(100, "After changer, status=%d\n", status);
182 if (status == 0) { /* did we succeed? */
183 rtn_stat = 1; /* tape loaded by changer */
186 rtn_stat = 0; /* no changer found */
188 free_pool_memory(changer);
192 free_pool_memory(changer);
198 * Returns: -1 if error from changer command
200 * Note, this is safe to do without releasing the drive
201 * since it does not attempt load/unload a slot.
203 int get_autochanger_loaded_slot(DCR *dcr)
206 POOLMEM *changer, *results;
208 uint32_t timeout = dcr->device->max_changer_wait;
209 int drive = dcr->dev->drive_index;
211 if (!dcr->device->changer_command) {
212 Jmsg(jcr, M_FATAL, 0, _("3992 Missing Changer command.\n"));
216 results = get_pool_memory(PM_MESSAGE);
217 changer = get_pool_memory(PM_FNAME);
220 /* Find out what is loaded, zero means device is unloaded */
222 Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded drive %d\" command.\n"),
224 changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded");
226 Dmsg1(100, "Run program=%s\n", changer);
227 status = run_program(changer, timeout, results);
228 Dmsg3(100, "run_prog: %s stat=%d result=%s\n", changer, status, results);
230 loaded = str_to_int32(results);
232 Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result is Slot %d.\n"),
234 dcr->dev->Slot = loaded;
236 Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result: nothing loaded.\n"),
242 be.set_errno(status);
243 Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded drive %d\" command: ERR=%s.\n"),
244 drive, be.strerror());
245 loaded = -1; /* force unload */
248 free_pool_memory(changer);
249 free_pool_memory(results);
253 static void lock_changer(DCR *dcr)
255 AUTOCHANGER *changer_res = dcr->device->changer_res;
257 Dmsg1(200, "Locking changer %s\n", changer_res->hdr.name);
258 P(changer_res->changer_mutex); /* Lock changer script */
262 static void unlock_changer(DCR *dcr)
264 AUTOCHANGER *changer_res = dcr->device->changer_res;
266 Dmsg1(200, "Unlocking changer %s\n", changer_res->hdr.name);
267 V(changer_res->changer_mutex); /* Unlock changer script */
272 * Unload the volume, if any, in this drive
273 * On entry: loaded == 0 -- nothing to do
274 * loaded < 0 -- check if anything to do
275 * loaded > 0 -- load slot == loaded
277 bool unload_autochanger(DCR *dcr, int loaded)
279 DEVICE *dev = dcr->dev;
282 uint32_t timeout = dcr->device->max_changer_wait;
289 if (!dev->is_autochanger() || !dcr->device->changer_name ||
290 !dcr->device->changer_command) {
295 loaded = get_autochanger_loaded_slot(dcr);
299 POOLMEM *changer = get_pool_memory(PM_FNAME);
302 _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
303 loaded, dev->drive_index);
304 slot = dcr->VolCatInfo.Slot;
305 dcr->VolCatInfo.Slot = loaded;
306 changer = edit_device_codes(dcr, changer,
307 dcr->device->changer_command, "unload");
309 Dmsg1(100, "Run program=%s\n", changer);
310 int stat = run_program(changer, timeout, NULL);
311 dcr->VolCatInfo.Slot = slot;
315 Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
316 slot, dev->drive_index, be.strerror());
319 dev->Slot = 0; /* nothing loaded */
321 free_pool_memory(changer);
328 * Unload the slot if mounted in a different drive
330 static bool unload_other_drive(DCR *dcr, int slot)
336 uint32_t timeout = dcr->device->max_changer_wait;
338 AUTOCHANGER *changer = dcr->dev->device->changer_res;
346 if (changer->device->size() == 1) {
350 foreach_alist(device, changer->device) {
351 if (device->dev && device->dev->Slot == slot) {
361 /* The Volume we want is on another device. */
362 if (dev->is_busy()) {
363 Dmsg4(100, "Vol %s for dev=%s in use dev=%s slot=%d\n",
364 dcr->VolumeName, dcr->dev->print_name(),
365 dev->print_name(), slot);
367 for (int i=0; i < 3; i++) {
368 if (dev->is_busy()) {
369 wait_for_device(dcr->jcr, first);
376 if (dev->is_busy()) {
377 Jmsg(jcr, M_WARNING, 0, _("Volume \"%s\" is in use by device %s\n"),
378 dcr->VolumeName, dev->print_name());
379 Dmsg4(100, "Vol %s for dev=%s is busy dev=%s slot=%d\n",
380 dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), slot);
381 Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->reserved_device);
386 POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
389 _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
390 slot, dev->drive_index);
392 Dmsg2(100, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
393 slot, dev->drive_index);
397 save_slot = dcr->VolCatInfo.Slot;
398 dcr->VolCatInfo.Slot = slot;
399 changer_cmd = edit_device_codes(dcr, changer_cmd,
400 dcr->device->changer_command, "unload");
402 Dmsg2(200, "close dev=%s reserve=%d\n", dev->print_name(),
403 dev->reserved_device);
404 Dmsg1(100, "Run program=%s\n", changer_cmd);
405 int stat = run_program(changer_cmd, timeout, NULL);
406 dcr->VolCatInfo.Slot = save_slot;
411 Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
412 slot, dev->drive_index, be.strerror());
414 Dmsg3(100, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
415 slot, dev->drive_index, be.strerror());
418 dev->Slot = 0; /* nothing loaded */
419 Dmsg0(100, "Slot unloaded\n");
423 free_pool_memory(changer_cmd);
430 * List the Volumes that are in the autoloader possibly
431 * with their barcodes.
432 * We assume that it is always the Console that is calling us.
434 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)
436 DEVICE *dev = dcr->dev;
437 uint32_t timeout = dcr->device->max_changer_wait;
440 int len = sizeof_pool_memory(dir->msg) - 1;
444 if (!dev->is_autochanger() || !dcr->device->changer_name ||
445 !dcr->device->changer_command) {
446 if (strcmp(cmd, "drives") == 0) {
447 bnet_fsend(dir, "drives=1\n");
449 bnet_fsend(dir, _("3993 Device %s not an autochanger device.\n"),
455 if (strcmp(cmd, "list") == 0) {
456 unload_autochanger(dcr, -1);
458 if (strcmp(cmd, "drives") == 0) {
459 AUTOCHANGER *changer_res = dcr->device->changer_res;
462 drives = changer_res->device->size();
464 bnet_fsend(dir, "drives=%d\n", drives);
465 Dmsg1(100, "drives=%d\n", drives);
469 changer = get_pool_memory(PM_FNAME);
471 /* Now issue the command */
472 changer = edit_device_codes(dcr, changer,
473 dcr->device->changer_command, cmd);
474 bnet_fsend(dir, _("3306 Issuing autochanger \"%s\" command.\n"), cmd);
475 bpipe = open_bpipe(changer, timeout, "r");
477 bnet_fsend(dir, _("3996 Open bpipe failed.\n"));
480 if (strcmp(cmd, "list") == 0) {
481 /* Get output from changer */
482 while (fgets(dir->msg, len, bpipe->rfd)) {
483 dir->msglen = strlen(dir->msg);
484 Dmsg1(100, "<stored: %s\n", dir->msg);
487 } else if (strcmp(cmd, "slots") == 0 ) {
489 /* For slots command, read a single line */
491 fgets(buf, sizeof(buf)-1, bpipe->rfd);
492 buf[sizeof(buf)-1] = 0;
493 /* Strip any leading space in front of # of slots */
494 for (p=buf; B_ISSPACE(*p); p++)
496 bnet_fsend(dir, "slots=%s", p);
497 Dmsg1(100, "<stored: %s", dir->msg);
500 stat = close_bpipe(bpipe);
504 bnet_fsend(dir, _("Autochanger error: ERR=%s\n"), be.strerror());
506 bnet_sig(dir, BNET_EOD);
511 free_pool_memory(changer);
517 * Edit codes into ChangerCommand
519 * %a = archive device name
520 * %c = changer device name
521 * %d = changer drive index
530 * omsg = edited output message
531 * imsg = input string containing edit codes (%x)
532 * cmd = command string (load, unload, ...)
535 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
542 Dmsg1(1800, "edit_device_codes: %s\n", imsg);
543 for (p=imsg; *p; p++) {
550 str = dcr->dev->archive_name();
553 str = NPRT(dcr->device->changer_name);
556 sprintf(add, "%d", dcr->dev->drive_index);
563 sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
567 sprintf(add, "%d", dcr->VolCatInfo.Slot);
570 case 'j': /* Job name */
574 str = NPRT(dcr->VolumeName);
577 str = NPRT(dcr->jcr->client_name);
592 Dmsg1(1900, "add_str %s\n", str);
593 pm_strcat(&omsg, (char *)str);
594 Dmsg1(1800, "omsg=%s\n", omsg);
596 Dmsg1(800, "omsg=%s\n", omsg);