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 */
186 Dmsg2(100, "load slot %d status=%d\n", slot, status);
189 status = 0; /* we got what we want */
190 dev->Slot = slot; /* set currently loaded slot */
192 Dmsg1(100, "After changer, status=%d\n", status);
193 if (status == 0) { /* did we succeed? */
194 rtn_stat = 1; /* tape loaded by changer */
197 free_pool_memory(changer);
201 free_pool_memory(changer);
207 * Returns: -1 if error from changer command
209 * Note, this is safe to do without releasing the drive
210 * since it does not attempt load/unload a slot.
212 int get_autochanger_loaded_slot(DCR *dcr)
215 POOLMEM *changer, *results;
217 uint32_t timeout = dcr->device->max_changer_wait;
218 int drive = dcr->dev->drive_index;
220 if (!dcr->device->changer_command) {
221 Jmsg(jcr, M_FATAL, 0, _("3992 Missing Changer command.\n"));
225 results = get_pool_memory(PM_MESSAGE);
226 changer = get_pool_memory(PM_FNAME);
229 /* Find out what is loaded, zero means device is unloaded */
231 Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded drive %d\" command.\n"),
233 changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded");
235 Dmsg1(100, "Run program=%s\n", changer);
236 status = run_program(changer, timeout, results);
237 Dmsg3(100, "run_prog: %s stat=%d result=%s\n", changer, status, results);
239 loaded = str_to_int32(results);
241 Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result is Slot %d.\n"),
243 dcr->dev->Slot = loaded;
245 Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result: nothing loaded.\n"),
251 be.set_errno(status);
252 Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded drive %d\" command: ERR=%s.\n"),
253 drive, be.strerror());
254 loaded = -1; /* force unload */
257 free_pool_memory(changer);
258 free_pool_memory(results);
262 static void lock_changer(DCR *dcr)
264 AUTOCHANGER *changer_res = dcr->device->changer_res;
266 Dmsg1(200, "Locking changer %s\n", changer_res->hdr.name);
267 P(changer_res->changer_mutex); /* Lock changer script */
271 static void unlock_changer(DCR *dcr)
273 AUTOCHANGER *changer_res = dcr->device->changer_res;
275 Dmsg1(200, "Unlocking changer %s\n", changer_res->hdr.name);
276 V(changer_res->changer_mutex); /* Unlock changer script */
281 * Unload the volume, if any, in this drive
282 * On entry: loaded == 0 -- nothing to do
283 * loaded < 0 -- check if anything to do
284 * loaded > 0 -- load slot == loaded
286 bool unload_autochanger(DCR *dcr, int loaded)
288 DEVICE *dev = dcr->dev;
291 uint32_t timeout = dcr->device->max_changer_wait;
298 if (!dev->is_autochanger() || !dcr->device->changer_name ||
299 !dcr->device->changer_command) {
304 loaded = get_autochanger_loaded_slot(dcr);
308 POOLMEM *changer = get_pool_memory(PM_FNAME);
311 _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
312 loaded, dev->drive_index);
313 slot = dcr->VolCatInfo.Slot;
314 dcr->VolCatInfo.Slot = loaded;
315 changer = edit_device_codes(dcr, changer,
316 dcr->device->changer_command, "unload");
318 Dmsg1(100, "Run program=%s\n", changer);
319 int stat = run_program(changer, timeout, NULL);
320 dcr->VolCatInfo.Slot = slot;
324 Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
325 slot, dev->drive_index, be.strerror());
328 dev->Slot = 0; /* nothing loaded */
330 free_pool_memory(changer);
337 * Unload the slot if mounted in a different drive
339 static bool unload_other_drive(DCR *dcr, int slot)
345 uint32_t timeout = dcr->device->max_changer_wait;
347 AUTOCHANGER *changer = dcr->dev->device->changer_res;
355 if (changer->device->size() == 1) {
359 foreach_alist(device, changer->device) {
360 if (device->dev && device->dev->Slot == slot) {
370 /* The Volume we want is on another device. */
371 if (dev->is_busy()) {
372 Dmsg4(100, "Vol %s for dev=%s in use dev=%s slot=%d\n",
373 dcr->VolumeName, dcr->dev->print_name(),
374 dev->print_name(), slot);
376 for (int i=0; i < 3; i++) {
377 if (dev->is_busy()) {
378 wait_for_device(dcr->jcr, first);
385 if (dev->is_busy()) {
386 Jmsg(jcr, M_WARNING, 0, _("Volume \"%s\" is in use by device %s\n"),
387 dcr->VolumeName, dev->print_name());
388 Dmsg4(100, "Vol %s for dev=%s is busy dev=%s slot=%d\n",
389 dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), slot);
390 Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->reserved_device);
395 POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
398 _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
399 slot, dev->drive_index);
401 Dmsg2(100, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
402 slot, dev->drive_index);
406 save_slot = dcr->VolCatInfo.Slot;
407 dcr->VolCatInfo.Slot = slot;
408 changer_cmd = edit_device_codes(dcr, changer_cmd,
409 dcr->device->changer_command, "unload");
411 Dmsg2(200, "close dev=%s reserve=%d\n", dev->print_name(),
412 dev->reserved_device);
413 Dmsg1(100, "Run program=%s\n", changer_cmd);
414 int stat = run_program(changer_cmd, timeout, NULL);
415 dcr->VolCatInfo.Slot = save_slot;
420 Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
421 slot, dev->drive_index, be.strerror());
423 Dmsg3(100, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
424 slot, dev->drive_index, be.strerror());
427 dev->Slot = 0; /* nothing loaded */
428 Dmsg0(100, "Slot unloaded\n");
432 free_pool_memory(changer_cmd);
439 * List the Volumes that are in the autoloader possibly
440 * with their barcodes.
441 * We assume that it is always the Console that is calling us.
443 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)
445 DEVICE *dev = dcr->dev;
446 uint32_t timeout = dcr->device->max_changer_wait;
449 int len = sizeof_pool_memory(dir->msg) - 1;
453 if (!dev->is_autochanger() || !dcr->device->changer_name ||
454 !dcr->device->changer_command) {
455 if (strcmp(cmd, "drives") == 0) {
456 bnet_fsend(dir, "drives=1\n");
458 bnet_fsend(dir, _("3993 Device %s not an autochanger device.\n"),
464 if (strcmp(cmd, "list") == 0) {
465 unload_autochanger(dcr, -1);
467 if (strcmp(cmd, "drives") == 0) {
468 AUTOCHANGER *changer_res = dcr->device->changer_res;
471 drives = changer_res->device->size();
473 bnet_fsend(dir, "drives=%d\n", drives);
474 Dmsg1(100, "drives=%d\n", drives);
478 changer = get_pool_memory(PM_FNAME);
480 /* Now issue the command */
481 changer = edit_device_codes(dcr, changer,
482 dcr->device->changer_command, cmd);
483 bnet_fsend(dir, _("3306 Issuing autochanger \"%s\" command.\n"), cmd);
484 bpipe = open_bpipe(changer, timeout, "r");
486 bnet_fsend(dir, _("3996 Open bpipe failed.\n"));
489 if (strcmp(cmd, "list") == 0) {
490 /* Get output from changer */
491 while (fgets(dir->msg, len, bpipe->rfd)) {
492 dir->msglen = strlen(dir->msg);
493 Dmsg1(100, "<stored: %s\n", dir->msg);
496 } else if (strcmp(cmd, "slots") == 0 ) {
498 /* For slots command, read a single line */
500 fgets(buf, sizeof(buf)-1, bpipe->rfd);
501 buf[sizeof(buf)-1] = 0;
502 /* Strip any leading space in front of # of slots */
503 for (p=buf; B_ISSPACE(*p); p++)
505 bnet_fsend(dir, "slots=%s", p);
506 Dmsg1(100, "<stored: %s", dir->msg);
509 stat = close_bpipe(bpipe);
513 bnet_fsend(dir, _("Autochanger error: ERR=%s\n"), be.strerror());
515 bnet_sig(dir, BNET_EOD);
520 free_pool_memory(changer);
526 * Edit codes into ChangerCommand
528 * %a = archive device name
529 * %c = changer device name
530 * %d = changer drive index
539 * omsg = edited output message
540 * imsg = input string containing edit codes (%x)
541 * cmd = command string (load, unload, ...)
544 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
551 Dmsg1(1800, "edit_device_codes: %s\n", imsg);
552 for (p=imsg; *p; p++) {
559 str = dcr->dev->archive_name();
562 str = NPRT(dcr->device->changer_name);
565 sprintf(add, "%d", dcr->dev->drive_index);
572 sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
576 sprintf(add, "%d", dcr->VolCatInfo.Slot);
579 case 'j': /* Job name */
583 str = NPRT(dcr->VolumeName);
586 str = NPRT(dcr->jcr->client_name);
601 Dmsg1(1900, "add_str %s\n", str);
602 pm_strcat(&omsg, (char *)str);
603 Dmsg1(1800, "omsg=%s\n", omsg);
605 Dmsg1(800, "omsg=%s\n", omsg);