]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/autochanger.c
cf78a72c1884b3bbe67ec4627db0de80184b88f2
[bacula/bacula] / bacula / src / stored / autochanger.c
1 /*
2  *
3  *  Routines for handling the autochanger.
4  *
5  *   Kern Sibbald, August MMII
6  *                            
7  *   Version $Id$
8  */
9 /*
10    Copyright (C) 2002-2005 Kern Sibbald
11
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.
16
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.
21
22  */
23
24 #include "bacula.h"                   /* pull in global headers */
25 #include "stored.h"                   /* pull in Storage Deamon headers */
26
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);
31
32 /*
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.
37  *
38  *  Note if dir is not NULL, it is the console requesting the
39  *   autoload for labeling, so we respond directly to the
40  *   dir bsock.
41  *
42  *  Returns: 1 on success
43  *           0 on failure (no changer available)
44  *          -1 on error on autochanger
45  */
46 int autoload_device(DCR *dcr, int writing, BSOCK *dir)
47 {
48    JCR *jcr = dcr->jcr;
49    DEVICE *dev = dcr->dev;
50    int slot;
51    int drive = dev->drive_index;
52    int rtn_stat = -1;                 /* error status */
53    POOLMEM *changer;
54
55    slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
56    /*
57     * Handle autoloaders here.  If we cannot autoload it, we
58     *  will return 0 so that the sysop will be asked to load it.
59     */
60    if (writing && dev->is_autochanger() && slot <= 0) {
61       if (dir) {
62          return 0;                    /* For user, bail out right now */
63       }
64       if (dir_find_next_appendable_volume(dcr)) {
65          slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
66       } else {
67          slot = 0;
68       }
69    }
70    Dmsg1(400, "Want changer slot=%d\n", slot);
71
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;
75       int loaded, status;
76
77       loaded = get_autochanger_loaded_slot(dcr);
78
79       if (loaded != slot) {
80          /* Unload anything in our drive */
81          if (!unload_autochanger(dcr, loaded)) {
82             goto bail_out;
83          }
84             
85          /* Make sure desired slot is unloaded */
86          if (!unload_other_drive(dcr, slot)) {
87             goto bail_out;
88          }
89
90          /*
91           * Load the desired cassette
92           */
93          Dmsg1(400, "Doing changer load slot %d\n", slot);
94          Jmsg(jcr, M_INFO, 0,
95               _("3304 Issuing autochanger \"load slot %d, drive %d\" command.\n"),
96               slot, drive);
97          dcr->VolCatInfo.Slot = slot;    /* slot to be loaded */
98          changer = edit_device_codes(dcr, changer, 
99                       dcr->device->changer_command, "load");
100          status = run_program(changer, timeout, NULL);
101          if (status == 0) {
102             Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"),
103                     slot, drive);
104             dev->Slot = slot;         /* set currently loaded slot */
105          } else {
106            berrno be;
107            be.set_errno(status);
108             Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": ERR=%s.\n"),
109                     slot, drive, be.strerror());
110             goto bail_out;
111          }
112          unlock_changer(dcr);
113          Dmsg2(400, "load slot %d status=%d\n", slot, status);
114       } else {
115          status = 0;                  /* we got what we want */
116          dev->Slot = slot;            /* set currently loaded slot */
117       }
118       Dmsg1(400, "After changer, status=%d\n", status);
119       if (status == 0) {              /* did we succeed? */
120          rtn_stat = 1;                /* tape loaded by changer */
121       }
122    } else {
123       rtn_stat = 0;                   /* no changer found */
124    }
125    free_pool_memory(changer);
126    return rtn_stat;
127
128 bail_out:
129    free_pool_memory(changer);
130    unlock_changer(dcr);
131    return -1;
132
133 }
134
135 /*
136  * Returns: -1 if error from changer command
137  *          slot otherwise
138  */
139 int get_autochanger_loaded_slot(DCR *dcr)
140 {
141    JCR *jcr = dcr->jcr;
142    POOLMEM *changer, *results;
143    int status, loaded;
144    uint32_t timeout = dcr->device->max_changer_wait;
145    int drive = dcr->dev->drive_index;
146
147    results = get_pool_memory(PM_MESSAGE);
148    changer = get_pool_memory(PM_FNAME);
149
150    lock_changer(dcr);
151
152    /* Find out what is loaded, zero means device is unloaded */
153    Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded drive %d\" command.\n"),
154         drive);
155    changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded");
156    *results = 0;
157    status = run_program(changer, timeout, results);
158    Dmsg3(50, "run_prog: %s stat=%d result=%s\n", changer, status, results);
159    if (status == 0) {
160       loaded = atoi(results);
161       if (loaded > 0) {
162          Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result is Slot %d.\n"),
163               drive, loaded);
164          dcr->dev->Slot = loaded;
165       } else {
166          Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result: nothing loaded.\n"),
167               drive);
168          dcr->dev->Slot = 0;
169       }
170    } else {
171       berrno be;
172       be.set_errno(status);
173       Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded drive %d\" command: ERR=%s.\n"),
174            drive, be.strerror());
175       loaded = -1;              /* force unload */
176    }
177    unlock_changer(dcr);
178    free_pool_memory(changer);
179    free_pool_memory(results);
180    return loaded;
181 }
182
183 static void lock_changer(DCR *dcr)
184 {
185    AUTOCHANGER *changer_res = dcr->device->changer_res;
186    if (changer_res) {
187       Dmsg1(100, "Locking changer %s\n", changer_res->hdr.name);
188       P(changer_res->changer_mutex);  /* Lock changer script */
189    }
190 }
191
192 static void unlock_changer(DCR *dcr)
193 {
194    AUTOCHANGER *changer_res = dcr->device->changer_res;
195    if (changer_res) {
196       Dmsg1(100, "Unlocking changer %s\n", changer_res->hdr.name);
197       V(changer_res->changer_mutex);  /* Unlock changer script */
198    }
199 }
200
201 /*
202  * Unload the volume, if any, in this drive
203  *  On entry: loaded == 0 -- nothing to do
204  *            loaded  < 0 -- check if anything to do
205  *            loaded  > 0 -- load slot == loaded
206  */
207 bool unload_autochanger(DCR *dcr, int loaded)
208 {
209    DEVICE *dev = dcr->dev;
210    JCR *jcr = dcr->jcr;
211    int slot;
212    uint32_t timeout = dcr->device->max_changer_wait;
213    bool ok = true;
214
215    if (loaded == 0) {
216       return true;
217    }
218
219    if (!dev->is_autochanger() || !dcr->device->changer_name ||
220        !dcr->device->changer_command) {
221       return false;
222    }
223
224    offline_or_rewind_dev(dev);
225    /* We are going to load a new tape, so close the device */
226    force_close_device(dev);
227
228    if (loaded < 0) {
229       loaded = get_autochanger_loaded_slot(dcr);
230    }
231    if (loaded > 0) {
232       POOLMEM *changer = get_pool_memory(PM_FNAME);
233       Jmsg(jcr, M_INFO, 0,
234            _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
235            loaded, dev->drive_index);
236       slot = dcr->VolCatInfo.Slot;
237       dcr->VolCatInfo.Slot = loaded;
238       changer = edit_device_codes(dcr, changer, 
239                    dcr->device->changer_command, "unload");
240       lock_changer(dcr);
241       int stat = run_program(changer, timeout, NULL);
242       unlock_changer(dcr);
243       dcr->VolCatInfo.Slot = slot;
244       if (stat != 0) {
245          berrno be;
246          be.set_errno(stat);
247          Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
248                  slot, dev->drive_index, be.strerror());
249          ok = false;
250       } else {
251          dev->Slot = 0;            /* nothing loaded */
252       }
253       free_pool_memory(changer);
254    }
255    return ok;
256 }
257
258 /*
259  * Unload the slot if mounted in a different drive
260  */
261 static bool unload_other_drive(DCR *dcr, int slot)
262 {
263    DEVICE *dev, *save_dev;
264    JCR *jcr = dcr->jcr;
265    int save_slot;
266    uint32_t timeout = dcr->device->max_changer_wait;
267    bool ok = true;
268    AUTOCHANGER *changer = dcr->dev->device->changer_res;
269    DEVRES *device;
270    bool found = false;
271
272    if (!changer) {
273       return false;
274    }
275    if (changer->device->size() == 1) {
276       return true;
277    }
278       
279    foreach_alist(device, changer->device) {
280       if (device->dev && device->dev->Slot == slot) {
281          found = true;
282          dev = device->dev;
283          break;
284       }
285    }
286    if (!found) {
287       return true;
288    }
289    if (dev->is_busy()) {
290       Jmsg(jcr, M_WARNING, 0, _("Volume %s is in use by device %s\n"),
291            dcr->VolumeName, dev->print_name());
292       Dmsg2(200, "Volume %s is in use by device %s\n",
293            dcr->VolumeName, dev->print_name());
294       
295       return false;
296    }
297
298    offline_or_rewind_dev(dev);
299    /* We are going to load a new tape, so close the device */
300    force_close_device(dev);
301
302    POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
303    Jmsg(jcr, M_INFO, 0,
304         _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
305         slot, dev->drive_index);
306
307    Dmsg2(200, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
308         slot, dev->drive_index);
309
310    save_slot = dcr->VolCatInfo.Slot;
311    save_dev = dcr->dev;
312    dcr->dev = dev;
313    dcr->VolCatInfo.Slot = slot;
314    changer_cmd = edit_device_codes(dcr, changer_cmd, 
315                 dcr->device->changer_command, "unload");
316    lock_changer(dcr);
317    Dmsg1(200, "Run program=%s\n", changer_cmd);
318    int stat = run_program(changer_cmd, timeout, NULL);
319    unlock_changer(dcr);
320    dcr->VolCatInfo.Slot = save_slot;
321    dcr->dev = save_dev;
322    if (stat != 0) {
323       berrno be;
324       be.set_errno(stat);
325       Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
326               slot, dev->drive_index, be.strerror());
327
328       Dmsg3(200, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
329               slot, dev->drive_index, be.strerror());
330       ok = false;
331    } else {
332       dev->Slot = 0;            /* nothing loaded */
333       Dmsg0(200, "Slot unloaded\n");
334    }
335    free_pool_memory(changer_cmd);
336    return ok;
337 }
338
339
340
341 /*
342  * List the Volumes that are in the autoloader possibly
343  *   with their barcodes.
344  *   We assume that it is always the Console that is calling us.
345  */
346 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)  
347 {
348    DEVICE *dev = dcr->dev;
349    uint32_t timeout = dcr->device->max_changer_wait;
350    POOLMEM *changer;
351    BPIPE *bpipe;
352    int len = sizeof_pool_memory(dir->msg) - 1;
353    bool ok = false;
354    int stat;
355
356    if (!dev->is_autochanger() || !dcr->device->changer_name ||
357        !dcr->device->changer_command) {
358       bnet_fsend(dir, _("3993 Device %s not an autochanger device.\n"),
359          dev->print_name());
360       return false;
361    }
362
363    changer = get_pool_memory(PM_FNAME);
364    /* List command? */
365    if (strcmp(cmd, "list") == 0) {
366       unload_autochanger(dcr, -1);
367    }
368
369    /* Now issue the command */
370    changer = edit_device_codes(dcr, changer, 
371                  dcr->device->changer_command, cmd);
372    bnet_fsend(dir, _("3306 Issuing autochanger \"%s\" command.\n"), cmd);
373    lock_changer(dcr);
374    bpipe = open_bpipe(changer, timeout, "r");
375    if (!bpipe) {
376       unlock_changer(dcr);
377       bnet_fsend(dir, _("3996 Open bpipe failed.\n"));
378       goto bail_out;
379    }
380    if (strcmp(cmd, "list") == 0) {
381       /* Get output from changer */
382       while (fgets(dir->msg, len, bpipe->rfd)) {
383          dir->msglen = strlen(dir->msg);
384          Dmsg1(100, "<stored: %s\n", dir->msg);
385          bnet_send(dir);
386       }
387    } else {
388       /* For slots command, read a single line */
389       bstrncpy(dir->msg, "slots=", len);
390       fgets(dir->msg+6, len-6, bpipe->rfd);
391       dir->msglen = strlen(dir->msg);
392       Dmsg1(100, "<stored: %s", dir->msg);
393       bnet_send(dir);
394    }
395                  
396    stat = close_bpipe(bpipe);
397    unlock_changer(dcr);
398    if (stat != 0) {
399       berrno be;
400       be.set_errno(stat);
401       bnet_fsend(dir, _("Autochanger error: ERR=%s\n"), be.strerror());
402    }
403    bnet_sig(dir, BNET_EOD);
404    ok = true;
405
406 bail_out:
407    free_pool_memory(changer);
408    return true;
409 }
410
411
412 /*
413  * Edit codes into ChangerCommand
414  *  %% = %
415  *  %a = archive device name
416  *  %c = changer device name
417  *  %d = changer drive index
418  *  %f = Client's name
419  *  %j = Job name
420  *  %o = command
421  *  %s = Slot base 0
422  *  %S = Slot base 1
423  *  %v = Volume name
424  *
425  *
426  *  omsg = edited output message
427  *  imsg = input string containing edit codes (%x)
428  *  cmd = command string (load, unload, ...)
429  *
430  */
431 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
432 {
433    const char *p;
434    const char *str;
435    char add[20];
436
437    *omsg = 0;
438    Dmsg1(1800, "edit_device_codes: %s\n", imsg);
439    for (p=imsg; *p; p++) {
440       if (*p == '%') {
441          switch (*++p) {
442          case '%':
443             str = "%";
444             break;
445          case 'a':
446             str = dcr->dev->archive_name();
447             break;
448          case 'c':
449             str = NPRT(dcr->device->changer_name);
450             break;
451          case 'd':
452             sprintf(add, "%d", dcr->dev->drive_index);
453             str = add;
454             break;
455          case 'o':
456             str = NPRT(cmd);
457             break;
458          case 's':
459             sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
460             str = add;
461             break;
462          case 'S':
463             sprintf(add, "%d", dcr->VolCatInfo.Slot);
464             str = add;
465             break;
466          case 'j':                    /* Job name */
467             str = dcr->jcr->Job;
468             break;
469          case 'v':
470             str = NPRT(dcr->VolumeName);
471             break;
472          case 'f':
473             str = NPRT(dcr->jcr->client_name);
474             break;
475
476          default:
477             add[0] = '%';
478             add[1] = *p;
479             add[2] = 0;
480             str = add;
481             break;
482          }
483       } else {
484          add[0] = *p;
485          add[1] = 0;
486          str = add;
487       }
488       Dmsg1(1900, "add_str %s\n", str);
489       pm_strcat(&omsg, (char *)str);
490       Dmsg1(1800, "omsg=%s\n", omsg);
491    }
492    Dmsg1(800, "omsg=%s\n", omsg);
493    return omsg;
494 }