3 * Routines for handling the autochanger.
5 * Kern Sibbald, August MMII
10 Copyright (C) 2002-2005 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 slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
109 * Handle autoloaders here. If we cannot autoload it, we
110 * will return 0 so that the sysop will be asked to load it.
112 if (writing && dev->is_autochanger() && slot <= 0) {
114 return 0; /* For user, bail out right now */
116 if (dir_find_next_appendable_volume(dcr)) {
117 slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
122 Dmsg1(400, "Want changer slot=%d\n", slot);
124 changer = get_pool_memory(PM_FNAME);
125 if (slot > 0 && dcr->device->changer_name && dcr->device->changer_command) {
126 uint32_t timeout = dcr->device->max_changer_wait;
129 loaded = get_autochanger_loaded_slot(dcr);
131 if (loaded != slot) {
133 /* Unload anything in our drive */
134 if (!unload_autochanger(dcr, loaded)) {
138 /* Make sure desired slot is unloaded */
139 if (!unload_other_drive(dcr, slot)) {
144 * Load the desired cassette
147 Dmsg1(400, "Doing changer load slot %d\n", slot);
149 _("3304 Issuing autochanger \"load slot %d, drive %d\" command.\n"),
151 dcr->VolCatInfo.Slot = slot; /* slot to be loaded */
152 changer = edit_device_codes(dcr, changer,
153 dcr->device->changer_command, "load");
154 status = run_program(changer, timeout, NULL);
156 Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"),
158 dev->Slot = slot; /* set currently loaded slot */
161 be.set_errno(status);
162 Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": ERR=%s.\n"),
163 slot, drive, be.strerror());
164 rtn_stat = -1; /* hard error */
166 Dmsg2(400, "load slot %d status=%d\n", slot, status);
169 status = 0; /* we got what we want */
170 dev->Slot = slot; /* set currently loaded slot */
172 Dmsg1(400, "After changer, status=%d\n", status);
173 if (status == 0) { /* did we succeed? */
174 rtn_stat = 1; /* tape loaded by changer */
177 rtn_stat = 0; /* no changer found */
179 free_pool_memory(changer);
183 free_pool_memory(changer);
189 * Returns: -1 if error from changer command
191 * Note, this is safe to do without releasing the drive
192 * since it does not attempt load/unload a slot.
194 int get_autochanger_loaded_slot(DCR *dcr)
197 POOLMEM *changer, *results;
199 uint32_t timeout = dcr->device->max_changer_wait;
200 int drive = dcr->dev->drive_index;
202 if (!dcr->device->changer_command) {
203 Jmsg(jcr, M_FATAL, 0, _("3992 Missing Changer command.\n"));
207 results = get_pool_memory(PM_MESSAGE);
208 changer = get_pool_memory(PM_FNAME);
211 /* Find out what is loaded, zero means device is unloaded */
213 Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded drive %d\" command.\n"),
215 changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded");
217 status = run_program(changer, timeout, results);
218 Dmsg3(50, "run_prog: %s stat=%d result=%s\n", changer, status, results);
220 loaded = atoi(results);
222 Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result is Slot %d.\n"),
224 dcr->dev->Slot = loaded;
226 Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result: nothing loaded.\n"),
232 be.set_errno(status);
233 Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded drive %d\" command: ERR=%s.\n"),
234 drive, be.strerror());
235 loaded = -1; /* force unload */
238 free_pool_memory(changer);
239 free_pool_memory(results);
243 static void lock_changer(DCR *dcr)
245 AUTOCHANGER *changer_res = dcr->device->changer_res;
247 Dmsg1(100, "Locking changer %s\n", changer_res->hdr.name);
248 P(changer_res->changer_mutex); /* Lock changer script */
252 static void unlock_changer(DCR *dcr)
254 AUTOCHANGER *changer_res = dcr->device->changer_res;
256 Dmsg1(100, "Unlocking changer %s\n", changer_res->hdr.name);
257 V(changer_res->changer_mutex); /* Unlock changer script */
262 * Unload the volume, if any, in this drive
263 * On entry: loaded == 0 -- nothing to do
264 * loaded < 0 -- check if anything to do
265 * loaded > 0 -- load slot == loaded
267 bool unload_autochanger(DCR *dcr, int loaded)
269 DEVICE *dev = dcr->dev;
272 uint32_t timeout = dcr->device->max_changer_wait;
279 if (!dev->is_autochanger() || !dcr->device->changer_name ||
280 !dcr->device->changer_command) {
285 loaded = get_autochanger_loaded_slot(dcr);
288 /* We are going to load a new tape, so close the device */
289 offline_or_rewind_dev(dev);
290 force_close_device(dev);
293 POOLMEM *changer = get_pool_memory(PM_FNAME);
296 _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
297 loaded, dev->drive_index);
298 slot = dcr->VolCatInfo.Slot;
299 dcr->VolCatInfo.Slot = loaded;
300 changer = edit_device_codes(dcr, changer,
301 dcr->device->changer_command, "unload");
302 int stat = run_program(changer, timeout, NULL);
303 dcr->VolCatInfo.Slot = slot;
307 Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
308 slot, dev->drive_index, be.strerror());
311 dev->Slot = 0; /* nothing loaded */
313 free_pool_memory(changer);
320 * Unload the slot if mounted in a different drive
322 static bool unload_other_drive(DCR *dcr, int slot)
328 uint32_t timeout = dcr->device->max_changer_wait;
330 AUTOCHANGER *changer = dcr->dev->device->changer_res;
337 if (changer->device->size() == 1) {
341 foreach_alist(device, changer->device) {
342 if (device->dev && device->dev->Slot == slot) {
351 if (dev->is_busy()) {
352 Jmsg(jcr, M_WARNING, 0, _("Volume %s is in use by device %s\n"),
353 dcr->VolumeName, dev->print_name());
354 Dmsg2(200, "Volume %s is in use by device %s\n",
355 dcr->VolumeName, dev->print_name());
360 /* We are going to unload a tape, so close the device */
361 offline_or_rewind_dev(dev);
362 force_close_device(dev);
364 POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
367 _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
368 slot, dev->drive_index);
370 Dmsg2(200, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
371 slot, dev->drive_index);
373 save_slot = dcr->VolCatInfo.Slot;
376 dcr->VolCatInfo.Slot = slot;
377 changer_cmd = edit_device_codes(dcr, changer_cmd,
378 dcr->device->changer_command, "unload");
379 Dmsg1(200, "Run program=%s\n", changer_cmd);
380 int stat = run_program(changer_cmd, timeout, NULL);
381 dcr->VolCatInfo.Slot = save_slot;
386 Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
387 slot, dev->drive_index, be.strerror());
389 Dmsg3(200, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
390 slot, dev->drive_index, be.strerror());
393 dev->Slot = 0; /* nothing loaded */
394 Dmsg0(200, "Slot unloaded\n");
397 free_pool_memory(changer_cmd);
404 * List the Volumes that are in the autoloader possibly
405 * with their barcodes.
406 * We assume that it is always the Console that is calling us.
408 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)
410 DEVICE *dev = dcr->dev;
411 uint32_t timeout = dcr->device->max_changer_wait;
414 int len = sizeof_pool_memory(dir->msg) - 1;
418 if (!dev->is_autochanger() || !dcr->device->changer_name ||
419 !dcr->device->changer_command) {
420 bnet_fsend(dir, _("3993 Device %s not an autochanger device.\n"),
426 if (strcmp(cmd, "list") == 0) {
427 unload_autochanger(dcr, -1);
429 if (strcmp(cmd, "drives") == 0) {
430 AUTOCHANGER *changer_res = dcr->device->changer_res;
433 drives = changer_res->device->size();
435 bnet_fsend(dir, "drives=%d\n", drives);
436 Dmsg1(100, "drives=%d\n", drives);
440 changer = get_pool_memory(PM_FNAME);
442 /* Now issue the command */
443 changer = edit_device_codes(dcr, changer,
444 dcr->device->changer_command, cmd);
445 bnet_fsend(dir, _("3306 Issuing autochanger \"%s\" command.\n"), cmd);
446 bpipe = open_bpipe(changer, timeout, "r");
448 bnet_fsend(dir, _("3996 Open bpipe failed.\n"));
451 if (strcmp(cmd, "list") == 0) {
452 /* Get output from changer */
453 while (fgets(dir->msg, len, bpipe->rfd)) {
454 dir->msglen = strlen(dir->msg);
455 Dmsg1(100, "<stored: %s\n", dir->msg);
458 } else if (strcmp(cmd, "slots") == 0 ) {
459 /* For slots command, read a single line */
460 bstrncpy(dir->msg, "slots=", len);
461 fgets(dir->msg+6, len-6, bpipe->rfd);
462 dir->msglen = strlen(dir->msg);
463 Dmsg1(100, "<stored: %s", dir->msg);
467 stat = close_bpipe(bpipe);
471 bnet_fsend(dir, _("Autochanger error: ERR=%s\n"), be.strerror());
473 bnet_sig(dir, BNET_EOD);
478 free_pool_memory(changer);
484 * Edit codes into ChangerCommand
486 * %a = archive device name
487 * %c = changer device name
488 * %d = changer drive index
497 * omsg = edited output message
498 * imsg = input string containing edit codes (%x)
499 * cmd = command string (load, unload, ...)
502 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
509 Dmsg1(1800, "edit_device_codes: %s\n", imsg);
510 for (p=imsg; *p; p++) {
517 str = dcr->dev->archive_name();
520 str = NPRT(dcr->device->changer_name);
523 sprintf(add, "%d", dcr->dev->drive_index);
530 sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
534 sprintf(add, "%d", dcr->VolCatInfo.Slot);
537 case 'j': /* Job name */
541 str = NPRT(dcr->VolumeName);
544 str = NPRT(dcr->jcr->client_name);
559 Dmsg1(1900, "add_str %s\n", str);
560 pm_strcat(&omsg, (char *)str);
561 Dmsg1(1800, "omsg=%s\n", omsg);
563 Dmsg1(800, "omsg=%s\n", omsg);