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);
32 * Called here to do an autoload using the autochanger, if
33 * configured, and if a Slot has been defined for this Volume.
34 * On success this routine loads the indicated tape, but the
35 * label is not read, so it must be verified.
37 * Note if dir is not NULL, it is the console requesting the
38 * autoload for labeling, so we respond directly to the
41 * Returns: 1 on success
42 * 0 on failure (no changer available)
43 * -1 on error on autochanger
45 int autoload_device(DCR *dcr, int writing, BSOCK *dir)
48 DEVICE *dev = dcr->dev;
50 int drive = dev->drive_index;
51 int rtn_stat = -1; /* error status */
54 slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
56 * Handle autoloaders here. If we cannot autoload it, we
57 * will return 0 so that the sysop will be asked to load it.
59 if (writing && dev->is_autochanger() && slot <= 0) {
61 return 0; /* For user, bail out right now */
63 if (dir_find_next_appendable_volume(dcr)) {
64 slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
69 Dmsg1(400, "Want changer slot=%d\n", slot);
71 changer = get_pool_memory(PM_FNAME);
72 if (slot > 0 && dcr->device->changer_name && dcr->device->changer_command) {
73 uint32_t timeout = dcr->device->max_changer_wait;
76 loaded = get_autochanger_loaded_slot(dcr);
78 /* If tape we want is not loaded, load it. */
80 if (!unload_autochanger(dcr, loaded)) {
84 * Load the desired cassette
86 Dmsg1(400, "Doing changer load slot %d\n", slot);
88 _("3304 Issuing autochanger \"load slot %d, drive %d\" command.\n"),
90 dcr->VolCatInfo.Slot = slot; /* slot to be loaded */
91 changer = edit_device_codes(dcr, changer,
92 dcr->device->changer_command, "load");
93 status = run_program(changer, timeout, NULL);
95 Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"),
97 dev->Slot = slot; /* set currently loaded slot */
100 be.set_errno(status);
101 Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": ERR=%s.\n"),
102 slot, drive, be.strerror());
106 Dmsg2(400, "load slot %d status=%d\n", slot, status);
108 status = 0; /* we got what we want */
109 dev->Slot = slot; /* set currently loaded slot */
111 Dmsg1(400, "After changer, status=%d\n", status);
112 if (status == 0) { /* did we succeed? */
113 rtn_stat = 1; /* tape loaded by changer */
116 rtn_stat = 0; /* no changer found */
118 free_pool_memory(changer);
122 free_pool_memory(changer);
129 * Returns: -1 if error from changer command
132 int get_autochanger_loaded_slot(DCR *dcr)
135 POOLMEM *changer, *results;
137 uint32_t timeout = dcr->device->max_changer_wait;
138 int drive = dcr->dev->drive_index;
140 results = get_pool_memory(PM_MESSAGE);
141 changer = get_pool_memory(PM_FNAME);
145 /* Find out what is loaded, zero means device is unloaded */
146 Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded drive %d\" command.\n"),
148 changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded");
150 status = run_program(changer, timeout, results);
151 Dmsg3(50, "run_prog: %s stat=%d result=%s\n", changer, status, results);
153 loaded = atoi(results);
155 Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result is Slot %d.\n"),
157 dcr->dev->Slot = loaded;
159 Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result: nothing loaded.\n"),
165 be.set_errno(status);
166 Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded drive %d\" command: ERR=%s.\n"),
167 drive, be.strerror());
168 loaded = -1; /* force unload */
171 free_pool_memory(changer);
172 free_pool_memory(results);
176 static void lock_changer(DCR *dcr)
178 AUTOCHANGER *changer_res = dcr->device->changer_res;
180 Dmsg1(100, "Locking changer %s\n", changer_res->hdr.name);
181 P(changer_res->changer_mutex); /* Lock changer script */
185 static void unlock_changer(DCR *dcr)
187 AUTOCHANGER *changer_res = dcr->device->changer_res;
189 Dmsg1(100, "Unlocking changer %s\n", changer_res->hdr.name);
190 V(changer_res->changer_mutex); /* Unlock changer script */
195 * Unload the volume, if any, in this drive
196 * On entry: loaded == 0 -- nothing to do
197 * loaded < 0 -- check if anything to do
198 * loaded > 0 -- load slot == loaded
200 bool unload_autochanger(DCR *dcr, int loaded)
202 DEVICE *dev = dcr->dev;
205 uint32_t timeout = dcr->device->max_changer_wait;
212 if (!dev->is_autochanger() || !dcr->device->changer_name ||
213 !dcr->device->changer_command) {
217 offline_or_rewind_dev(dev);
218 /* We are going to load a new tape, so close the device */
219 force_close_device(dev);
222 loaded = get_autochanger_loaded_slot(dcr);
225 POOLMEM *changer = get_pool_memory(PM_FNAME);
227 _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
228 loaded, dev->drive_index);
229 slot = dcr->VolCatInfo.Slot;
230 dcr->VolCatInfo.Slot = loaded;
231 changer = edit_device_codes(dcr, changer,
232 dcr->device->changer_command, "unload");
234 int stat = run_program(changer, timeout, NULL);
236 dcr->VolCatInfo.Slot = slot;
240 Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
241 slot, dev->drive_index, be.strerror());
244 dev->Slot = 0; /* nothing loaded */
246 free_pool_memory(changer);
253 * List the Volumes that are in the autoloader possibly
254 * with their barcodes.
255 * We assume that it is always the Console that is calling us.
257 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)
259 DEVICE *dev = dcr->dev;
260 uint32_t timeout = dcr->device->max_changer_wait;
263 int len = sizeof_pool_memory(dir->msg) - 1;
267 if (!dev->is_autochanger() || !dcr->device->changer_name ||
268 !dcr->device->changer_command) {
269 bnet_fsend(dir, _("3993 Device %s not an autochanger device.\n"),
274 changer = get_pool_memory(PM_FNAME);
276 if (strcmp(cmd, "list") == 0) {
277 unload_autochanger(dcr, -1);
280 /* Now issue the command */
281 changer = edit_device_codes(dcr, changer,
282 dcr->device->changer_command, cmd);
283 bnet_fsend(dir, _("3306 Issuing autochanger \"%s\" command.\n"), cmd);
285 bpipe = open_bpipe(changer, timeout, "r");
288 bnet_fsend(dir, _("3996 Open bpipe failed.\n"));
291 if (strcmp(cmd, "list") == 0) {
292 /* Get output from changer */
293 while (fgets(dir->msg, len, bpipe->rfd)) {
294 dir->msglen = strlen(dir->msg);
295 Dmsg1(100, "<stored: %s\n", dir->msg);
299 /* For slots command, read a single line */
300 bstrncpy(dir->msg, "slots=", len);
301 fgets(dir->msg+6, len-6, bpipe->rfd);
302 dir->msglen = strlen(dir->msg);
303 Dmsg1(100, "<stored: %s", dir->msg);
307 stat = close_bpipe(bpipe);
312 bnet_fsend(dir, _("Autochanger error: ERR=%s\n"), be.strerror());
314 bnet_sig(dir, BNET_EOD);
318 free_pool_memory(changer);
324 * Edit codes into ChangerCommand
326 * %a = archive device name
327 * %c = changer device name
328 * %d = changer drive index
337 * omsg = edited output message
338 * imsg = input string containing edit codes (%x)
339 * cmd = command string (load, unload, ...)
342 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
349 Dmsg1(1800, "edit_device_codes: %s\n", imsg);
350 for (p=imsg; *p; p++) {
357 str = dcr->dev->archive_name();
360 str = NPRT(dcr->device->changer_name);
363 sprintf(add, "%d", dcr->dev->drive_index);
370 sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
374 sprintf(add, "%d", dcr->VolCatInfo.Slot);
377 case 'j': /* Job name */
381 str = NPRT(dcr->VolumeName);
384 str = NPRT(dcr->jcr->client_name);
399 Dmsg1(1900, "add_str %s\n", str);
400 pm_strcat(&omsg, (char *)str);
401 Dmsg1(1800, "omsg=%s\n", omsg);
403 Dmsg1(800, "omsg=%s\n", omsg);