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