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);
33 * Called here to do an autoload using the autochanger, if
34 * configured, and if a Slot has been defined for this Volume.
35 * On success this routine loads the indicated tape, but the
36 * label is not read, so it must be verified.
38 * Note if dir is not NULL, it is the console requesting the
39 * autoload for labeling, so we respond directly to the
42 * Returns: 1 on success
43 * 0 on failure (no changer available)
44 * -1 on error on autochanger
46 int autoload_device(DCR *dcr, int writing, BSOCK *dir)
49 DEVICE *dev = dcr->dev;
51 int drive = dev->drive_index;
52 int rtn_stat = -1; /* error status */
55 slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
57 * Handle autoloaders here. If we cannot autoload it, we
58 * will return 0 so that the sysop will be asked to load it.
60 if (writing && dev->is_autochanger() && slot <= 0) {
62 return 0; /* For user, bail out right now */
64 if (dir_find_next_appendable_volume(dcr)) {
65 slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
70 Dmsg1(400, "Want changer slot=%d\n", slot);
72 changer = get_pool_memory(PM_FNAME);
73 if (slot > 0 && dcr->device->changer_name && dcr->device->changer_command) {
74 uint32_t timeout = dcr->device->max_changer_wait;
77 loaded = get_autochanger_loaded_slot(dcr);
80 /* Unload anything in our drive */
81 if (!unload_autochanger(dcr, loaded)) {
85 /* Make sure desired slot is unloaded */
86 if (!unload_other_drive(dcr, slot)) {
90 /* We are going to load a new tape, so close the device */
91 offline_or_rewind_dev(dev);
92 force_close_device(dev);
95 * Load the desired cassette
97 Dmsg1(400, "Doing changer load slot %d\n", slot);
99 _("3304 Issuing autochanger \"load slot %d, drive %d\" command.\n"),
101 dcr->VolCatInfo.Slot = slot; /* slot to be loaded */
102 changer = edit_device_codes(dcr, changer,
103 dcr->device->changer_command, "load");
104 status = run_program(changer, timeout, NULL);
106 Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"),
108 dev->Slot = slot; /* set currently loaded slot */
111 be.set_errno(status);
112 Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": ERR=%s.\n"),
113 slot, drive, be.strerror());
117 Dmsg2(400, "load slot %d status=%d\n", slot, status);
119 status = 0; /* we got what we want */
120 dev->Slot = slot; /* set currently loaded slot */
122 Dmsg1(400, "After changer, status=%d\n", status);
123 if (status == 0) { /* did we succeed? */
124 rtn_stat = 1; /* tape loaded by changer */
127 rtn_stat = 0; /* no changer found */
129 free_pool_memory(changer);
133 free_pool_memory(changer);
140 * Returns: -1 if error from changer command
143 int get_autochanger_loaded_slot(DCR *dcr)
146 POOLMEM *changer, *results;
148 uint32_t timeout = dcr->device->max_changer_wait;
149 int drive = dcr->dev->drive_index;
151 results = get_pool_memory(PM_MESSAGE);
152 changer = get_pool_memory(PM_FNAME);
156 /* Find out what is loaded, zero means device is unloaded */
157 Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded drive %d\" command.\n"),
159 changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded");
161 status = run_program(changer, timeout, results);
162 Dmsg3(50, "run_prog: %s stat=%d result=%s\n", changer, status, results);
164 loaded = atoi(results);
166 Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result is Slot %d.\n"),
168 dcr->dev->Slot = loaded;
170 Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result: nothing loaded.\n"),
176 be.set_errno(status);
177 Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded drive %d\" command: ERR=%s.\n"),
178 drive, be.strerror());
179 loaded = -1; /* force unload */
182 free_pool_memory(changer);
183 free_pool_memory(results);
187 static void lock_changer(DCR *dcr)
189 AUTOCHANGER *changer_res = dcr->device->changer_res;
191 Dmsg1(100, "Locking changer %s\n", changer_res->hdr.name);
192 P(changer_res->changer_mutex); /* Lock changer script */
196 static void unlock_changer(DCR *dcr)
198 AUTOCHANGER *changer_res = dcr->device->changer_res;
200 Dmsg1(100, "Unlocking changer %s\n", changer_res->hdr.name);
201 V(changer_res->changer_mutex); /* Unlock changer script */
206 * Unload the volume, if any, in this drive
207 * On entry: loaded == 0 -- nothing to do
208 * loaded < 0 -- check if anything to do
209 * loaded > 0 -- load slot == loaded
211 bool unload_autochanger(DCR *dcr, int loaded)
213 DEVICE *dev = dcr->dev;
216 uint32_t timeout = dcr->device->max_changer_wait;
223 if (!dev->is_autochanger() || !dcr->device->changer_name ||
224 !dcr->device->changer_command) {
228 /* We are going to load a new tape, so close the device */
229 offline_or_rewind_dev(dev);
230 force_close_device(dev);
233 loaded = get_autochanger_loaded_slot(dcr);
236 POOLMEM *changer = get_pool_memory(PM_FNAME);
238 _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
239 loaded, dev->drive_index);
240 slot = dcr->VolCatInfo.Slot;
241 dcr->VolCatInfo.Slot = loaded;
242 changer = edit_device_codes(dcr, changer,
243 dcr->device->changer_command, "unload");
245 int stat = run_program(changer, timeout, NULL);
247 dcr->VolCatInfo.Slot = slot;
251 Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
252 slot, dev->drive_index, be.strerror());
255 dev->Slot = 0; /* nothing loaded */
257 free_pool_memory(changer);
263 * Unload the slot if mounted in a different drive
265 static bool unload_other_drive(DCR *dcr, int slot)
271 uint32_t timeout = dcr->device->max_changer_wait;
273 AUTOCHANGER *changer = dcr->dev->device->changer_res;
280 if (changer->device->size() == 1) {
284 foreach_alist(device, changer->device) {
285 if (device->dev && device->dev->Slot == slot) {
294 if (dev->is_busy()) {
295 Jmsg(jcr, M_WARNING, 0, _("Volume %s is in use by device %s\n"),
296 dcr->VolumeName, dev->print_name());
297 Dmsg2(200, "Volume %s is in use by device %s\n",
298 dcr->VolumeName, dev->print_name());
303 /* We are going to unload a tape, so close the device */
304 offline_or_rewind_dev(dev);
305 force_close_device(dev);
307 POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
309 _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
310 slot, dev->drive_index);
312 Dmsg2(200, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
313 slot, dev->drive_index);
315 save_slot = dcr->VolCatInfo.Slot;
318 dcr->VolCatInfo.Slot = slot;
319 changer_cmd = edit_device_codes(dcr, changer_cmd,
320 dcr->device->changer_command, "unload");
322 Dmsg1(200, "Run program=%s\n", changer_cmd);
323 int stat = run_program(changer_cmd, timeout, NULL);
325 dcr->VolCatInfo.Slot = save_slot;
330 Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
331 slot, dev->drive_index, be.strerror());
333 Dmsg3(200, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
334 slot, dev->drive_index, be.strerror());
337 dev->Slot = 0; /* nothing loaded */
338 Dmsg0(200, "Slot unloaded\n");
340 free_pool_memory(changer_cmd);
347 * List the Volumes that are in the autoloader possibly
348 * with their barcodes.
349 * We assume that it is always the Console that is calling us.
351 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)
353 DEVICE *dev = dcr->dev;
354 uint32_t timeout = dcr->device->max_changer_wait;
357 int len = sizeof_pool_memory(dir->msg) - 1;
361 if (!dev->is_autochanger() || !dcr->device->changer_name ||
362 !dcr->device->changer_command) {
363 bnet_fsend(dir, _("3993 Device %s not an autochanger device.\n"),
368 changer = get_pool_memory(PM_FNAME);
370 if (strcmp(cmd, "list") == 0) {
371 unload_autochanger(dcr, -1);
374 /* Now issue the command */
375 changer = edit_device_codes(dcr, changer,
376 dcr->device->changer_command, cmd);
377 bnet_fsend(dir, _("3306 Issuing autochanger \"%s\" command.\n"), cmd);
379 bpipe = open_bpipe(changer, timeout, "r");
382 bnet_fsend(dir, _("3996 Open bpipe failed.\n"));
385 if (strcmp(cmd, "list") == 0) {
386 /* Get output from changer */
387 while (fgets(dir->msg, len, bpipe->rfd)) {
388 dir->msglen = strlen(dir->msg);
389 Dmsg1(100, "<stored: %s\n", dir->msg);
393 /* For slots command, read a single line */
394 bstrncpy(dir->msg, "slots=", len);
395 fgets(dir->msg+6, len-6, bpipe->rfd);
396 dir->msglen = strlen(dir->msg);
397 Dmsg1(100, "<stored: %s", dir->msg);
401 stat = close_bpipe(bpipe);
406 bnet_fsend(dir, _("Autochanger error: ERR=%s\n"), be.strerror());
408 bnet_sig(dir, BNET_EOD);
412 free_pool_memory(changer);
418 * Edit codes into ChangerCommand
420 * %a = archive device name
421 * %c = changer device name
422 * %d = changer drive index
431 * omsg = edited output message
432 * imsg = input string containing edit codes (%x)
433 * cmd = command string (load, unload, ...)
436 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
443 Dmsg1(1800, "edit_device_codes: %s\n", imsg);
444 for (p=imsg; *p; p++) {
451 str = dcr->dev->archive_name();
454 str = NPRT(dcr->device->changer_name);
457 sprintf(add, "%d", dcr->dev->drive_index);
464 sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
468 sprintf(add, "%d", dcr->VolCatInfo.Slot);
471 case 'j': /* Job name */
475 str = NPRT(dcr->VolumeName);
478 str = NPRT(dcr->jcr->client_name);
493 Dmsg1(1900, "add_str %s\n", str);
494 pm_strcat(&omsg, (char *)str);
495 Dmsg1(1800, "omsg=%s\n", omsg);
497 Dmsg1(800, "omsg=%s\n", omsg);