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