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