]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/autochanger.c
- Landon's fix for NLS detection
[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 = NULL;
264    DEVICE *save_dev;
265    JCR *jcr = dcr->jcr;
266    int save_slot;
267    uint32_t timeout = dcr->device->max_changer_wait;
268    bool ok = true;
269    AUTOCHANGER *changer = dcr->dev->device->changer_res;
270    DEVRES *device;
271    bool found = false;
272
273    if (!changer) {
274       return false;
275    }
276    if (changer->device->size() == 1) {
277       return true;
278    }
279       
280    foreach_alist(device, changer->device) {
281       if (device->dev && device->dev->Slot == slot) {
282          found = true;
283          dev = device->dev;
284          break;
285       }
286    }
287    if (!found) {
288       return true;
289    }
290    if (dev->is_busy()) {
291       Jmsg(jcr, M_WARNING, 0, _("Volume %s is in use by device %s\n"),
292            dcr->VolumeName, dev->print_name());
293       Dmsg2(200, "Volume %s is in use by device %s\n",
294            dcr->VolumeName, dev->print_name());
295       
296       return false;
297    }
298
299    offline_or_rewind_dev(dev);
300    /* We are going to load a new tape, so close the device */
301    force_close_device(dev);
302
303    POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
304    Jmsg(jcr, M_INFO, 0,
305         _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
306         slot, dev->drive_index);
307
308    Dmsg2(200, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
309         slot, dev->drive_index);
310
311    save_slot = dcr->VolCatInfo.Slot;
312    save_dev = dcr->dev;
313    dcr->dev = dev;
314    dcr->VolCatInfo.Slot = slot;
315    changer_cmd = edit_device_codes(dcr, changer_cmd, 
316                 dcr->device->changer_command, "unload");
317    lock_changer(dcr);
318    Dmsg1(200, "Run program=%s\n", changer_cmd);
319    int stat = run_program(changer_cmd, timeout, NULL);
320    unlock_changer(dcr);
321    dcr->VolCatInfo.Slot = save_slot;
322    dcr->dev = save_dev;
323    if (stat != 0) {
324       berrno be;
325       be.set_errno(stat);
326       Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
327               slot, dev->drive_index, be.strerror());
328
329       Dmsg3(200, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
330               slot, dev->drive_index, be.strerror());
331       ok = false;
332    } else {
333       dev->Slot = 0;            /* nothing loaded */
334       Dmsg0(200, "Slot unloaded\n");
335    }
336    free_pool_memory(changer_cmd);
337    return ok;
338 }
339
340
341
342 /*
343  * List the Volumes that are in the autoloader possibly
344  *   with their barcodes.
345  *   We assume that it is always the Console that is calling us.
346  */
347 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)  
348 {
349    DEVICE *dev = dcr->dev;
350    uint32_t timeout = dcr->device->max_changer_wait;
351    POOLMEM *changer;
352    BPIPE *bpipe;
353    int len = sizeof_pool_memory(dir->msg) - 1;
354    bool ok = false;
355    int stat;
356
357    if (!dev->is_autochanger() || !dcr->device->changer_name ||
358        !dcr->device->changer_command) {
359       bnet_fsend(dir, _("3993 Device %s not an autochanger device.\n"),
360          dev->print_name());
361       return false;
362    }
363
364    changer = get_pool_memory(PM_FNAME);
365    /* List command? */
366    if (strcmp(cmd, "list") == 0) {
367       unload_autochanger(dcr, -1);
368    }
369
370    /* Now issue the command */
371    changer = edit_device_codes(dcr, changer, 
372                  dcr->device->changer_command, cmd);
373    bnet_fsend(dir, _("3306 Issuing autochanger \"%s\" command.\n"), cmd);
374    lock_changer(dcr);
375    bpipe = open_bpipe(changer, timeout, "r");
376    if (!bpipe) {
377       unlock_changer(dcr);
378       bnet_fsend(dir, _("3996 Open bpipe failed.\n"));
379       goto bail_out;
380    }
381    if (strcmp(cmd, "list") == 0) {
382       /* Get output from changer */
383       while (fgets(dir->msg, len, bpipe->rfd)) {
384          dir->msglen = strlen(dir->msg);
385          Dmsg1(100, "<stored: %s\n", dir->msg);
386          bnet_send(dir);
387       }
388    } else {
389       /* For slots command, read a single line */
390       bstrncpy(dir->msg, "slots=", len);
391       fgets(dir->msg+6, len-6, bpipe->rfd);
392       dir->msglen = strlen(dir->msg);
393       Dmsg1(100, "<stored: %s", dir->msg);
394       bnet_send(dir);
395    }
396                  
397    stat = close_bpipe(bpipe);
398    unlock_changer(dcr);
399    if (stat != 0) {
400       berrno be;
401       be.set_errno(stat);
402       bnet_fsend(dir, _("Autochanger error: ERR=%s\n"), be.strerror());
403    }
404    bnet_sig(dir, BNET_EOD);
405    ok = true;
406
407 bail_out:
408    free_pool_memory(changer);
409    return true;
410 }
411
412
413 /*
414  * Edit codes into ChangerCommand
415  *  %% = %
416  *  %a = archive device name
417  *  %c = changer device name
418  *  %d = changer drive index
419  *  %f = Client's name
420  *  %j = Job name
421  *  %o = command
422  *  %s = Slot base 0
423  *  %S = Slot base 1
424  *  %v = Volume name
425  *
426  *
427  *  omsg = edited output message
428  *  imsg = input string containing edit codes (%x)
429  *  cmd = command string (load, unload, ...)
430  *
431  */
432 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
433 {
434    const char *p;
435    const char *str;
436    char add[20];
437
438    *omsg = 0;
439    Dmsg1(1800, "edit_device_codes: %s\n", imsg);
440    for (p=imsg; *p; p++) {
441       if (*p == '%') {
442          switch (*++p) {
443          case '%':
444             str = "%";
445             break;
446          case 'a':
447             str = dcr->dev->archive_name();
448             break;
449          case 'c':
450             str = NPRT(dcr->device->changer_name);
451             break;
452          case 'd':
453             sprintf(add, "%d", dcr->dev->drive_index);
454             str = add;
455             break;
456          case 'o':
457             str = NPRT(cmd);
458             break;
459          case 's':
460             sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
461             str = add;
462             break;
463          case 'S':
464             sprintf(add, "%d", dcr->VolCatInfo.Slot);
465             str = add;
466             break;
467          case 'j':                    /* Job name */
468             str = dcr->jcr->Job;
469             break;
470          case 'v':
471             str = NPRT(dcr->VolumeName);
472             break;
473          case 'f':
474             str = NPRT(dcr->jcr->client_name);
475             break;
476
477          default:
478             add[0] = '%';
479             add[1] = *p;
480             add[2] = 0;
481             str = add;
482             break;
483          }
484       } else {
485          add[0] = *p;
486          add[1] = 0;
487          str = add;
488       }
489       Dmsg1(1900, "add_str %s\n", str);
490       pm_strcat(&omsg, (char *)str);
491       Dmsg1(1800, "omsg=%s\n", omsg);
492    }
493    Dmsg1(800, "omsg=%s\n", omsg);
494    return omsg;
495 }