]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/autochanger.c
15Jan06
[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-2006 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          offline_or_rewind_dev(dev);
155          force_close_device(dev);
156          status = run_program(changer, timeout, NULL);
157          if (status == 0) {
158             Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"),
159                     slot, drive);
160             dev->Slot = slot;         /* set currently loaded slot */
161          } else {
162            berrno be;
163            be.set_errno(status);
164             Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": ERR=%s.\n"),
165                     slot, drive, be.strerror());
166             rtn_stat = -1;            /* hard error */
167          }
168          Dmsg2(400, "load slot %d status=%d\n", slot, status);
169          unlock_changer(dcr);
170       } else {
171          status = 0;                  /* we got what we want */
172          dev->Slot = slot;            /* set currently loaded slot */
173       }
174       Dmsg1(400, "After changer, status=%d\n", status);
175       if (status == 0) {              /* did we succeed? */
176          rtn_stat = 1;                /* tape loaded by changer */
177       }
178    } else {
179       rtn_stat = 0;                   /* no changer found */
180    }
181    free_pool_memory(changer);
182    return rtn_stat;
183
184 bail_out:
185    free_pool_memory(changer);
186    return -1;
187
188 }
189
190 /*
191  * Returns: -1 if error from changer command
192  *          slot otherwise
193  *  Note, this is safe to do without releasing the drive
194  *   since it does not attempt load/unload a slot.
195  */
196 int get_autochanger_loaded_slot(DCR *dcr)
197 {
198    JCR *jcr = dcr->jcr;
199    POOLMEM *changer, *results;
200    int status, loaded;
201    uint32_t timeout = dcr->device->max_changer_wait;
202    int drive = dcr->dev->drive_index;
203
204    if (!dcr->device->changer_command) {
205       Jmsg(jcr, M_FATAL, 0, _("3992 Missing Changer command.\n"));
206       return -1;
207    }
208
209    results = get_pool_memory(PM_MESSAGE);
210    changer = get_pool_memory(PM_FNAME);
211
212
213    /* Find out what is loaded, zero means device is unloaded */
214    lock_changer(dcr);
215    Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded drive %d\" command.\n"),
216         drive);
217    changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded");
218    *results = 0;
219    status = run_program(changer, timeout, results);
220    Dmsg3(50, "run_prog: %s stat=%d result=%s\n", changer, status, results);
221    if (status == 0) {
222       loaded = str_to_int32(results);
223       if (loaded > 0) {
224          Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result is Slot %d.\n"),
225               drive, loaded);
226          dcr->dev->Slot = loaded;
227       } else {
228          Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result: nothing loaded.\n"),
229               drive);
230          dcr->dev->Slot = 0;
231       }
232    } else {
233       berrno be;
234       be.set_errno(status);
235       Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded drive %d\" command: ERR=%s.\n"),
236            drive, be.strerror());
237       loaded = -1;              /* force unload */
238    }
239    unlock_changer(dcr);
240    free_pool_memory(changer);
241    free_pool_memory(results);
242    return loaded;
243 }
244
245 static void lock_changer(DCR *dcr)
246 {
247    AUTOCHANGER *changer_res = dcr->device->changer_res;
248    if (changer_res) {
249       Dmsg1(100, "Locking changer %s\n", changer_res->hdr.name);
250       P(changer_res->changer_mutex);  /* Lock changer script */
251    }
252 }
253
254 static void unlock_changer(DCR *dcr)
255 {
256    AUTOCHANGER *changer_res = dcr->device->changer_res;
257    if (changer_res) {
258       Dmsg1(100, "Unlocking changer %s\n", changer_res->hdr.name);
259       V(changer_res->changer_mutex);  /* Unlock changer script */
260    }
261 }
262
263 /*
264  * Unload the volume, if any, in this drive
265  *  On entry: loaded == 0 -- nothing to do
266  *            loaded  < 0 -- check if anything to do
267  *            loaded  > 0 -- load slot == loaded
268  */
269 bool unload_autochanger(DCR *dcr, int loaded)
270 {
271    DEVICE *dev = dcr->dev;
272    JCR *jcr = dcr->jcr;
273    int slot;
274    uint32_t timeout = dcr->device->max_changer_wait;
275    bool ok = true;
276
277    if (loaded == 0) {
278       return true;
279    }
280
281    if (!dev->is_autochanger() || !dcr->device->changer_name ||
282        !dcr->device->changer_command) {
283       return false;
284    }
285
286    if (loaded < 0) {
287       loaded = get_autochanger_loaded_slot(dcr);
288    }
289
290    if (loaded > 0) {
291       POOLMEM *changer = get_pool_memory(PM_FNAME);
292       lock_changer(dcr);
293       Jmsg(jcr, M_INFO, 0,
294            _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
295            loaded, dev->drive_index);
296       slot = dcr->VolCatInfo.Slot;
297       dcr->VolCatInfo.Slot = loaded;
298       changer = edit_device_codes(dcr, changer, 
299                    dcr->device->changer_command, "unload");
300       offline_or_rewind_dev(dev);
301       force_close_device(dev);
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    POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
361    lock_changer(dcr);
362    Jmsg(jcr, M_INFO, 0,
363         _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
364         slot, dev->drive_index);
365
366    Dmsg2(200, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
367         slot, dev->drive_index);
368
369    save_slot = dcr->VolCatInfo.Slot;
370    save_dev = dcr->dev;
371    dcr->dev = dev;
372    dcr->VolCatInfo.Slot = slot;
373    changer_cmd = edit_device_codes(dcr, changer_cmd, 
374                 dcr->device->changer_command, "unload");
375    Dmsg1(200, "Run program=%s\n", changer_cmd);
376    offline_or_rewind_dev(dev);
377    force_close_device(dev);
378    int stat = run_program(changer_cmd, timeout, NULL);
379    dcr->VolCatInfo.Slot = save_slot;
380    dcr->dev = save_dev;
381    if (stat != 0) {
382       berrno be;
383       be.set_errno(stat);
384       Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
385               slot, dev->drive_index, be.strerror());
386
387       Dmsg3(200, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
388               slot, dev->drive_index, be.strerror());
389       ok = false;
390    } else {
391       dev->Slot = 0;            /* nothing loaded */
392       Dmsg0(200, "Slot unloaded\n");
393    }
394    unlock_changer(dcr);
395    free_pool_memory(changer_cmd);
396    return ok;
397 }
398
399
400
401 /*
402  * List the Volumes that are in the autoloader possibly
403  *   with their barcodes.
404  *   We assume that it is always the Console that is calling us.
405  */
406 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)  
407 {
408    DEVICE *dev = dcr->dev;
409    uint32_t timeout = dcr->device->max_changer_wait;
410    POOLMEM *changer;
411    BPIPE *bpipe;
412    int len = sizeof_pool_memory(dir->msg) - 1;
413    bool ok = false;
414    int stat;
415
416    if (!dev->is_autochanger() || !dcr->device->changer_name ||
417        !dcr->device->changer_command) {
418       if (strcmp(cmd, "drives") == 0) {
419          bnet_fsend(dir, "drives=1\n");
420       }
421       bnet_fsend(dir, _("3993 Device %s not an autochanger device.\n"),
422          dev->print_name());
423       return false;
424    }
425
426    /* List command? */
427    if (strcmp(cmd, "list") == 0) {
428       unload_autochanger(dcr, -1);
429    }
430    if (strcmp(cmd, "drives") == 0) {
431       AUTOCHANGER *changer_res = dcr->device->changer_res;
432       int drives = 1;
433       if (changer_res) {
434          drives = changer_res->device->size();
435       }
436       bnet_fsend(dir, "drives=%d\n", drives);
437       Dmsg1(100, "drives=%d\n", drives);
438       return true;
439    }
440
441    changer = get_pool_memory(PM_FNAME);
442    lock_changer(dcr);
443    /* Now issue the command */
444    changer = edit_device_codes(dcr, changer, 
445                  dcr->device->changer_command, cmd);
446    bnet_fsend(dir, _("3306 Issuing autochanger \"%s\" command.\n"), cmd);
447    bpipe = open_bpipe(changer, timeout, "r");
448    if (!bpipe) {
449       bnet_fsend(dir, _("3996 Open bpipe failed.\n"));
450       goto bail_out;
451    }
452    if (strcmp(cmd, "list") == 0) {
453       /* Get output from changer */
454       while (fgets(dir->msg, len, bpipe->rfd)) {
455          dir->msglen = strlen(dir->msg);
456          Dmsg1(100, "<stored: %s\n", dir->msg);
457          bnet_send(dir);
458       }
459    } else if (strcmp(cmd, "slots") == 0 ) {
460       char buf[100], *p;
461       /* For slots command, read a single line */
462       buf[0] = 0;
463       fgets(buf, sizeof(buf)-1, bpipe->rfd);
464       buf[sizeof(buf)-1] = 0;
465       /* Strip any leading space in front of # of slots */
466       for (p=buf; B_ISSPACE(*p); p++)
467         { }
468       bnet_fsend(dir, "slots=%s", p);
469       Dmsg1(100, "<stored: %s", dir->msg);
470    } 
471                  
472    stat = close_bpipe(bpipe);
473    if (stat != 0) {
474       berrno be;
475       be.set_errno(stat);
476       bnet_fsend(dir, _("Autochanger error: ERR=%s\n"), be.strerror());
477    }
478    bnet_sig(dir, BNET_EOD);
479    ok = true;
480
481 bail_out:
482    unlock_changer(dcr);
483    free_pool_memory(changer);
484    return true;
485 }
486
487
488 /*
489  * Edit codes into ChangerCommand
490  *  %% = %
491  *  %a = archive device name
492  *  %c = changer device name
493  *  %d = changer drive index
494  *  %f = Client's name
495  *  %j = Job name
496  *  %o = command
497  *  %s = Slot base 0
498  *  %S = Slot base 1
499  *  %v = Volume name
500  *
501  *
502  *  omsg = edited output message
503  *  imsg = input string containing edit codes (%x)
504  *  cmd = command string (load, unload, ...)
505  *
506  */
507 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
508 {
509    const char *p;
510    const char *str;
511    char add[20];
512
513    *omsg = 0;
514    Dmsg1(1800, "edit_device_codes: %s\n", imsg);
515    for (p=imsg; *p; p++) {
516       if (*p == '%') {
517          switch (*++p) {
518          case '%':
519             str = "%";
520             break;
521          case 'a':
522             str = dcr->dev->archive_name();
523             break;
524          case 'c':
525             str = NPRT(dcr->device->changer_name);
526             break;
527          case 'd':
528             sprintf(add, "%d", dcr->dev->drive_index);
529             str = add;
530             break;
531          case 'o':
532             str = NPRT(cmd);
533             break;
534          case 's':
535             sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
536             str = add;
537             break;
538          case 'S':
539             sprintf(add, "%d", dcr->VolCatInfo.Slot);
540             str = add;
541             break;
542          case 'j':                    /* Job name */
543             str = dcr->jcr->Job;
544             break;
545          case 'v':
546             str = NPRT(dcr->VolumeName);
547             break;
548          case 'f':
549             str = NPRT(dcr->jcr->client_name);
550             break;
551
552          default:
553             add[0] = '%';
554             add[1] = *p;
555             add[2] = 0;
556             str = add;
557             break;
558          }
559       } else {
560          add[0] = *p;
561          add[1] = 0;
562          str = add;
563       }
564       Dmsg1(1900, "add_str %s\n", str);
565       pm_strcat(&omsg, (char *)str);
566       Dmsg1(1800, "omsg=%s\n", omsg);
567    }
568    Dmsg1(800, "omsg=%s\n", omsg);
569    return omsg;
570 }