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