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