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