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 Dmsg1(200, "Device %s is not an autochanger\n", dev->print_name());
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) {
147 POOL_MEM results(PM_MESSAGE);
149 /* Unload anything in our drive */
150 if (!unload_autochanger(dcr, loaded)) {
154 /* Make sure desired slot is unloaded */
155 if (!unload_other_drive(dcr, slot)) {
160 * Load the desired cassette
163 Dmsg1(100, "Doing changer load slot %d\n", slot);
165 _("3304 Issuing autochanger \"load slot %d, drive %d\" command.\n"),
167 dcr->VolCatInfo.Slot = slot; /* slot to be loaded */
168 changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "load");
170 Dmsg1(200, "Run program=%s\n", changer);
171 status = run_program_full_output(changer, timeout, results.c_str());
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\": "
183 "ERR=%s.\nResults=%s\n"),
184 slot, drive, be.strerror(), results.c_str());
185 rtn_stat = -1; /* hard error */
186 dev->Slot = -1; /* mark unknown */
188 Dmsg2(100, "load slot %d status=%d\n", slot, status);
191 status = 0; /* we got what we want */
192 dev->Slot = slot; /* set currently loaded slot */
194 Dmsg1(100, "After changer, status=%d\n", status);
195 if (status == 0) { /* did we succeed? */
196 rtn_stat = 1; /* tape loaded by changer */
199 free_pool_memory(changer);
203 free_pool_memory(changer);
209 * Returns: -1 if error from changer command
211 * Note, this is safe to do without releasing the drive
212 * since it does not attempt load/unload a slot.
214 int get_autochanger_loaded_slot(DCR *dcr)
217 DEVICE *dev = dcr->dev;
219 uint32_t timeout = dcr->device->max_changer_wait;
220 int drive = dcr->dev->drive_index;
221 POOL_MEM results(PM_MESSAGE);
224 if (!dev->is_autochanger()) {
227 if (!dcr->device->changer_command) {
228 Jmsg(jcr, M_FATAL, 0, _("3992 Missing Changer command.\n"));
235 /* Find out what is loaded, zero means device is unloaded */
236 changer = get_pool_memory(PM_FNAME);
238 Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded? drive %d\" command.\n"),
240 changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded");
241 *results.c_str() = 0;
242 Dmsg1(100, "Run program=%s\n", changer);
243 status = run_program_full_output(changer, timeout, results.c_str());
244 Dmsg3(100, "run_prog: %s stat=%d result=%s\n", changer, status, results.c_str());
246 loaded = str_to_int32(results.c_str());
248 Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result is Slot %d.\n"),
252 Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result: nothing loaded.\n"),
254 dev->Slot = -1; /* unknown */
258 be.set_errno(status);
259 Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded? drive %d\" command: "
260 "ERR=%s.\nResults=%s\n"), drive, be.strerror(), results.c_str());
261 loaded = -1; /* force unload */
264 free_pool_memory(changer);
268 static void lock_changer(DCR *dcr)
270 AUTOCHANGER *changer_res = dcr->device->changer_res;
272 Dmsg1(200, "Locking changer %s\n", changer_res->hdr.name);
273 P(changer_res->changer_mutex); /* Lock changer script */
277 static void unlock_changer(DCR *dcr)
279 AUTOCHANGER *changer_res = dcr->device->changer_res;
281 Dmsg1(200, "Unlocking changer %s\n", changer_res->hdr.name);
282 V(changer_res->changer_mutex); /* Unlock changer script */
287 * Unload the volume, if any, in this drive
288 * On entry: loaded == 0 -- nothing to do
289 * loaded < 0 -- check if anything to do
290 * loaded > 0 -- load slot == loaded
292 bool unload_autochanger(DCR *dcr, int loaded)
294 DEVICE *dev = dcr->dev;
297 uint32_t timeout = dcr->device->max_changer_wait;
304 if (!dev->is_autochanger() || !dcr->device->changer_name ||
305 !dcr->device->changer_command) {
310 loaded = get_autochanger_loaded_slot(dcr);
314 POOL_MEM results(PM_MESSAGE);
315 POOLMEM *changer = get_pool_memory(PM_FNAME);
318 _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
319 loaded, dev->drive_index);
320 slot = dcr->VolCatInfo.Slot;
321 dcr->VolCatInfo.Slot = loaded;
322 changer = edit_device_codes(dcr, changer,
323 dcr->device->changer_command, "unload");
325 Dmsg1(100, "Run program=%s\n", changer);
326 *results.c_str() = 0;
327 int stat = run_program_full_output(changer, timeout, results.c_str());
328 dcr->VolCatInfo.Slot = slot;
332 Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": "
333 "ERR=%s\nResults=%s\n"),
334 loaded, dev->drive_index, be.strerror(), results.c_str());
336 dev->Slot = -1; /* unknown */
338 dev->Slot = 0; /* nothing loaded */
340 free_pool_memory(changer);
347 * Unload the slot if mounted in a different drive
349 static bool unload_other_drive(DCR *dcr, int slot)
355 uint32_t timeout = dcr->device->max_changer_wait;
357 AUTOCHANGER *changer = dcr->dev->device->changer_res;
365 if (changer->device->size() == 1) {
369 foreach_alist(device, changer->device) {
370 if (device->dev && device->dev->Slot == slot) {
380 /* The Volume we want is on another device. */
381 if (dev->is_busy()) {
382 Dmsg4(100, "Vol %s for dev=%s in use dev=%s slot=%d\n",
383 dcr->VolumeName, dcr->dev->print_name(),
384 dev->print_name(), slot);
386 for (int i=0; i < 3; i++) {
387 if (dev->is_busy()) {
388 wait_for_device(dcr->jcr, first);
395 if (dev->is_busy()) {
396 Jmsg(jcr, M_WARNING, 0, _("Volume \"%s\" is in use by device %s\n"),
397 dcr->VolumeName, dev->print_name());
398 Dmsg4(100, "Vol %s for dev=%s is busy dev=%s slot=%d\n",
399 dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), slot);
400 Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->reserved_device);
405 POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
406 POOL_MEM results(PM_MESSAGE);
409 _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
410 slot, dev->drive_index);
412 Dmsg2(100, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
413 slot, dev->drive_index);
417 save_slot = dcr->VolCatInfo.Slot;
418 dcr->VolCatInfo.Slot = slot;
419 changer_cmd = edit_device_codes(dcr, changer_cmd,
420 dcr->device->changer_command, "unload");
422 Dmsg2(200, "close dev=%s reserve=%d\n", dev->print_name(),
423 dev->reserved_device);
424 Dmsg1(100, "Run program=%s\n", changer_cmd);
425 int stat = run_program_full_output(changer_cmd, timeout, results.c_str());
426 dcr->VolCatInfo.Slot = save_slot;
431 Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
432 slot, dev->drive_index, be.strerror());
434 Dmsg3(100, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
435 slot, dev->drive_index, be.strerror());
437 dev->Slot = -1; /* unknown */
439 dev->Slot = 0; /* nothing loaded */
440 Dmsg0(100, "Slot unloaded\n");
444 free_pool_memory(changer_cmd);
451 * List the Volumes that are in the autoloader possibly
452 * with their barcodes.
453 * We assume that it is always the Console that is calling us.
455 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)
457 DEVICE *dev = dcr->dev;
458 uint32_t timeout = dcr->device->max_changer_wait;
461 int len = sizeof_pool_memory(dir->msg) - 1;
465 if (!dev->is_autochanger() || !dcr->device->changer_name ||
466 !dcr->device->changer_command) {
467 if (strcmp(cmd, "drives") == 0) {
468 bnet_fsend(dir, "drives=1\n");
470 bnet_fsend(dir, _("3993 Device %s not an autochanger device.\n"),
475 if (strcmp(cmd, "drives") == 0) {
476 AUTOCHANGER *changer_res = dcr->device->changer_res;
479 drives = changer_res->device->size();
481 bnet_fsend(dir, "drives=%d\n", drives);
482 Dmsg1(100, "drives=%d\n", drives);
486 changer = get_pool_memory(PM_FNAME);
488 /* Now issue the command */
489 changer = edit_device_codes(dcr, changer,
490 dcr->device->changer_command, cmd);
491 bnet_fsend(dir, _("3306 Issuing autochanger \"%s\" command.\n"), cmd);
492 bpipe = open_bpipe(changer, timeout, "r");
494 bnet_fsend(dir, _("3996 Open bpipe failed.\n"));
497 if (strcmp(cmd, "list") == 0) {
498 /* Get output from changer */
499 while (fgets(dir->msg, len, bpipe->rfd)) {
500 dir->msglen = strlen(dir->msg);
501 Dmsg1(100, "<stored: %s\n", dir->msg);
504 } else if (strcmp(cmd, "slots") == 0 ) {
506 /* For slots command, read a single line */
508 fgets(buf, sizeof(buf)-1, bpipe->rfd);
509 buf[sizeof(buf)-1] = 0;
510 /* Strip any leading space in front of # of slots */
511 for (p=buf; B_ISSPACE(*p); p++)
513 bnet_fsend(dir, "slots=%s", p);
514 Dmsg1(100, "<stored: %s", dir->msg);
517 stat = close_bpipe(bpipe);
521 bnet_fsend(dir, _("Autochanger error: ERR=%s\n"), be.strerror());
523 bnet_sig(dir, BNET_EOD);
528 free_pool_memory(changer);
534 * Edit codes into ChangerCommand
536 * %a = archive device name
537 * %c = changer device name
538 * %d = changer drive index
547 * omsg = edited output message
548 * imsg = input string containing edit codes (%x)
549 * cmd = command string (load, unload, ...)
552 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
559 Dmsg1(1800, "edit_device_codes: %s\n", imsg);
560 for (p=imsg; *p; p++) {
567 str = dcr->dev->archive_name();
570 str = NPRT(dcr->device->changer_name);
573 sprintf(add, "%d", dcr->dev->drive_index);
580 sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
584 sprintf(add, "%d", dcr->VolCatInfo.Slot);
587 case 'j': /* Job name */
591 str = NPRT(dcr->VolumeName);
594 str = NPRT(dcr->jcr->client_name);
609 Dmsg1(1900, "add_str %s\n", str);
610 pm_strcat(&omsg, (char *)str);
611 Dmsg1(1800, "omsg=%s\n", omsg);
613 Dmsg1(800, "omsg=%s\n", omsg);