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) {
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());
335 dev->Slot = -1; /* unknown */
337 dev->Slot = 0; /* nothing loaded */
339 free_pool_memory(changer);
346 * Unload the slot if mounted in a different drive
348 static bool unload_other_drive(DCR *dcr, int slot)
354 uint32_t timeout = dcr->device->max_changer_wait;
356 AUTOCHANGER *changer = dcr->dev->device->changer_res;
364 if (changer->device->size() == 1) {
368 foreach_alist(device, changer->device) {
369 if (device->dev && device->dev->Slot == slot) {
379 /* The Volume we want is on another device. */
380 if (dev->is_busy()) {
381 Dmsg4(100, "Vol %s for dev=%s in use dev=%s slot=%d\n",
382 dcr->VolumeName, dcr->dev->print_name(),
383 dev->print_name(), slot);
385 for (int i=0; i < 3; i++) {
386 if (dev->is_busy()) {
387 wait_for_device(dcr->jcr, first);
394 if (dev->is_busy()) {
395 Jmsg(jcr, M_WARNING, 0, _("Volume \"%s\" is in use by device %s\n"),
396 dcr->VolumeName, dev->print_name());
397 Dmsg4(100, "Vol %s for dev=%s is busy dev=%s slot=%d\n",
398 dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), slot);
399 Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->reserved_device);
404 POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
407 _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
408 slot, dev->drive_index);
410 Dmsg2(100, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
411 slot, dev->drive_index);
415 save_slot = dcr->VolCatInfo.Slot;
416 dcr->VolCatInfo.Slot = slot;
417 changer_cmd = edit_device_codes(dcr, changer_cmd,
418 dcr->device->changer_command, "unload");
420 Dmsg2(200, "close dev=%s reserve=%d\n", dev->print_name(),
421 dev->reserved_device);
422 Dmsg1(100, "Run program=%s\n", changer_cmd);
423 int stat = run_program(changer_cmd, timeout, NULL);
424 dcr->VolCatInfo.Slot = save_slot;
429 Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
430 slot, dev->drive_index, be.strerror());
432 Dmsg3(100, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
433 slot, dev->drive_index, be.strerror());
435 dev->Slot = -1; /* unknown */
437 dev->Slot = 0; /* nothing loaded */
438 Dmsg0(100, "Slot unloaded\n");
442 free_pool_memory(changer_cmd);
449 * List the Volumes that are in the autoloader possibly
450 * with their barcodes.
451 * We assume that it is always the Console that is calling us.
453 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)
455 DEVICE *dev = dcr->dev;
456 uint32_t timeout = dcr->device->max_changer_wait;
459 int len = sizeof_pool_memory(dir->msg) - 1;
463 if (!dev->is_autochanger() || !dcr->device->changer_name ||
464 !dcr->device->changer_command) {
465 if (strcmp(cmd, "drives") == 0) {
466 bnet_fsend(dir, "drives=1\n");
468 bnet_fsend(dir, _("3993 Device %s not an autochanger device.\n"),
473 if (strcmp(cmd, "drives") == 0) {
474 AUTOCHANGER *changer_res = dcr->device->changer_res;
477 drives = changer_res->device->size();
479 bnet_fsend(dir, "drives=%d\n", drives);
480 Dmsg1(100, "drives=%d\n", drives);
484 changer = get_pool_memory(PM_FNAME);
486 /* Now issue the command */
487 changer = edit_device_codes(dcr, changer,
488 dcr->device->changer_command, cmd);
489 bnet_fsend(dir, _("3306 Issuing autochanger \"%s\" command.\n"), cmd);
490 bpipe = open_bpipe(changer, timeout, "r");
492 bnet_fsend(dir, _("3996 Open bpipe failed.\n"));
495 if (strcmp(cmd, "list") == 0) {
496 /* Get output from changer */
497 while (fgets(dir->msg, len, bpipe->rfd)) {
498 dir->msglen = strlen(dir->msg);
499 Dmsg1(100, "<stored: %s\n", dir->msg);
502 } else if (strcmp(cmd, "slots") == 0 ) {
504 /* For slots command, read a single line */
506 fgets(buf, sizeof(buf)-1, bpipe->rfd);
507 buf[sizeof(buf)-1] = 0;
508 /* Strip any leading space in front of # of slots */
509 for (p=buf; B_ISSPACE(*p); p++)
511 bnet_fsend(dir, "slots=%s", p);
512 Dmsg1(100, "<stored: %s", dir->msg);
515 stat = close_bpipe(bpipe);
519 bnet_fsend(dir, _("Autochanger error: ERR=%s\n"), be.strerror());
521 bnet_sig(dir, BNET_EOD);
526 free_pool_memory(changer);
532 * Edit codes into ChangerCommand
534 * %a = archive device name
535 * %c = changer device name
536 * %d = changer drive index
545 * omsg = edited output message
546 * imsg = input string containing edit codes (%x)
547 * cmd = command string (load, unload, ...)
550 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
557 Dmsg1(1800, "edit_device_codes: %s\n", imsg);
558 for (p=imsg; *p; p++) {
565 str = dcr->dev->archive_name();
568 str = NPRT(dcr->device->changer_name);
571 sprintf(add, "%d", dcr->dev->drive_index);
578 sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
582 sprintf(add, "%d", dcr->VolCatInfo.Slot);
585 case 'j': /* Job name */
589 str = NPRT(dcr->VolumeName);
592 str = NPRT(dcr->jcr->client_name);
607 Dmsg1(1900, "add_str %s\n", str);
608 pm_strcat(&omsg, (char *)str);
609 Dmsg1(1800, "omsg=%s\n", omsg);
611 Dmsg1(800, "omsg=%s\n", omsg);