]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/autochanger.c
Insure that all the SD tools init the Autochanger
[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          /* Unload anything in our drive */
132          if (!unload_autochanger(dcr, loaded)) {
133             goto bail_out;
134          }
135             
136          /* Make sure desired slot is unloaded */
137          if (!unload_other_drive(dcr, slot)) {
138             goto bail_out;
139          }
140
141          /* We are going to load a new tape, so close the device */
142          offline_or_rewind_dev(dev);
143          force_close_device(dev);
144
145          /*
146           * Load the desired cassette
147           */
148          Dmsg1(400, "Doing changer load slot %d\n", slot);
149          Jmsg(jcr, M_INFO, 0,
150               _("3304 Issuing autochanger \"load slot %d, drive %d\" command.\n"),
151               slot, drive);
152          dcr->VolCatInfo.Slot = slot;    /* slot to be loaded */
153          changer = edit_device_codes(dcr, changer, 
154                       dcr->device->changer_command, "load");
155          status = run_program(changer, timeout, NULL);
156          if (status == 0) {
157             Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"),
158                     slot, drive);
159             dev->Slot = slot;         /* set currently loaded slot */
160          } else {
161            berrno be;
162            be.set_errno(status);
163             Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": ERR=%s.\n"),
164                     slot, drive, be.strerror());
165             goto bail_out;
166          }
167          unlock_changer(dcr);
168          Dmsg2(400, "load slot %d status=%d\n", slot, status);
169       } else {
170          status = 0;                  /* we got what we want */
171          dev->Slot = slot;            /* set currently loaded slot */
172       }
173       Dmsg1(400, "After changer, status=%d\n", status);
174       if (status == 0) {              /* did we succeed? */
175          rtn_stat = 1;                /* tape loaded by changer */
176       }
177    } else {
178       rtn_stat = 0;                   /* no changer found */
179    }
180    free_pool_memory(changer);
181    return rtn_stat;
182
183 bail_out:
184    free_pool_memory(changer);
185    unlock_changer(dcr);
186    return -1;
187
188 }
189
190 /*
191  * Returns: -1 if error from changer command
192  *          slot otherwise
193  */
194 int get_autochanger_loaded_slot(DCR *dcr)
195 {
196    JCR *jcr = dcr->jcr;
197    POOLMEM *changer, *results;
198    int status, loaded;
199    uint32_t timeout = dcr->device->max_changer_wait;
200    int drive = dcr->dev->drive_index;
201
202    if (!dcr->device->changer_command) {
203       Jmsg(jcr, M_FATAL, 0, _("3992 Missing Changer command.\n"));
204       return -1;
205    }
206
207    results = get_pool_memory(PM_MESSAGE);
208    changer = get_pool_memory(PM_FNAME);
209
210    lock_changer(dcr);
211
212    /* Find out what is loaded, zero means device is unloaded */
213    Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded drive %d\" command.\n"),
214         drive);
215    changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded");
216    *results = 0;
217    status = run_program(changer, timeout, results);
218    Dmsg3(50, "run_prog: %s stat=%d result=%s\n", changer, status, results);
219    if (status == 0) {
220       loaded = atoi(results);
221       if (loaded > 0) {
222          Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result is Slot %d.\n"),
223               drive, loaded);
224          dcr->dev->Slot = loaded;
225       } else {
226          Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result: nothing loaded.\n"),
227               drive);
228          dcr->dev->Slot = 0;
229       }
230    } else {
231       berrno be;
232       be.set_errno(status);
233       Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded drive %d\" command: ERR=%s.\n"),
234            drive, be.strerror());
235       loaded = -1;              /* force unload */
236    }
237    unlock_changer(dcr);
238    free_pool_memory(changer);
239    free_pool_memory(results);
240    return loaded;
241 }
242
243 static void lock_changer(DCR *dcr)
244 {
245    AUTOCHANGER *changer_res = dcr->device->changer_res;
246    if (changer_res) {
247       Dmsg1(100, "Locking changer %s\n", changer_res->hdr.name);
248       P(changer_res->changer_mutex);  /* Lock changer script */
249    }
250 }
251
252 static void unlock_changer(DCR *dcr)
253 {
254    AUTOCHANGER *changer_res = dcr->device->changer_res;
255    if (changer_res) {
256       Dmsg1(100, "Unlocking changer %s\n", changer_res->hdr.name);
257       V(changer_res->changer_mutex);  /* Unlock changer script */
258    }
259 }
260
261 /*
262  * Unload the volume, if any, in this drive
263  *  On entry: loaded == 0 -- nothing to do
264  *            loaded  < 0 -- check if anything to do
265  *            loaded  > 0 -- load slot == loaded
266  */
267 bool unload_autochanger(DCR *dcr, int loaded)
268 {
269    DEVICE *dev = dcr->dev;
270    JCR *jcr = dcr->jcr;
271    int slot;
272    uint32_t timeout = dcr->device->max_changer_wait;
273    bool ok = true;
274
275    if (loaded == 0) {
276       return true;
277    }
278
279    if (!dev->is_autochanger() || !dcr->device->changer_name ||
280        !dcr->device->changer_command) {
281       return false;
282    }
283
284    /* We are going to load a new tape, so close the device */
285    offline_or_rewind_dev(dev);
286    force_close_device(dev);
287
288    if (loaded < 0) {
289       loaded = get_autochanger_loaded_slot(dcr);
290    }
291    if (loaded > 0) {
292       POOLMEM *changer = get_pool_memory(PM_FNAME);
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       lock_changer(dcr);
301       int stat = run_program(changer, timeout, NULL);
302       unlock_changer(dcr);
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    }
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    Jmsg(jcr, M_INFO, 0,
365         _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
366         slot, dev->drive_index);
367
368    Dmsg2(200, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
369         slot, dev->drive_index);
370
371    save_slot = dcr->VolCatInfo.Slot;
372    save_dev = dcr->dev;
373    dcr->dev = dev;
374    dcr->VolCatInfo.Slot = slot;
375    changer_cmd = edit_device_codes(dcr, changer_cmd, 
376                 dcr->device->changer_command, "unload");
377    lock_changer(dcr);
378    Dmsg1(200, "Run program=%s\n", changer_cmd);
379    int stat = run_program(changer_cmd, timeout, NULL);
380    unlock_changer(dcr);
381    dcr->VolCatInfo.Slot = save_slot;
382    dcr->dev = save_dev;
383    if (stat != 0) {
384       berrno be;
385       be.set_errno(stat);
386       Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
387               slot, dev->drive_index, be.strerror());
388
389       Dmsg3(200, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
390               slot, dev->drive_index, be.strerror());
391       ok = false;
392    } else {
393       dev->Slot = 0;            /* nothing loaded */
394       Dmsg0(200, "Slot unloaded\n");
395    }
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    changer = get_pool_memory(PM_FNAME);
425    /* List command? */
426    if (strcmp(cmd, "list") == 0) {
427       unload_autochanger(dcr, -1);
428    }
429
430    /* Now issue the command */
431    changer = edit_device_codes(dcr, changer, 
432                  dcr->device->changer_command, cmd);
433    bnet_fsend(dir, _("3306 Issuing autochanger \"%s\" command.\n"), cmd);
434    lock_changer(dcr);
435    bpipe = open_bpipe(changer, timeout, "r");
436    if (!bpipe) {
437       unlock_changer(dcr);
438       bnet_fsend(dir, _("3996 Open bpipe failed.\n"));
439       goto bail_out;
440    }
441    if (strcmp(cmd, "list") == 0) {
442       /* Get output from changer */
443       while (fgets(dir->msg, len, bpipe->rfd)) {
444          dir->msglen = strlen(dir->msg);
445          Dmsg1(100, "<stored: %s\n", dir->msg);
446          bnet_send(dir);
447       }
448    } else {
449       /* For slots command, read a single line */
450       bstrncpy(dir->msg, "slots=", len);
451       fgets(dir->msg+6, len-6, bpipe->rfd);
452       dir->msglen = strlen(dir->msg);
453       Dmsg1(100, "<stored: %s", dir->msg);
454       bnet_send(dir);
455    }
456                  
457    stat = close_bpipe(bpipe);
458    unlock_changer(dcr);
459    if (stat != 0) {
460       berrno be;
461       be.set_errno(stat);
462       bnet_fsend(dir, _("Autochanger error: ERR=%s\n"), be.strerror());
463    }
464    bnet_sig(dir, BNET_EOD);
465    ok = true;
466
467 bail_out:
468    free_pool_memory(changer);
469    return true;
470 }
471
472
473 /*
474  * Edit codes into ChangerCommand
475  *  %% = %
476  *  %a = archive device name
477  *  %c = changer device name
478  *  %d = changer drive index
479  *  %f = Client's name
480  *  %j = Job name
481  *  %o = command
482  *  %s = Slot base 0
483  *  %S = Slot base 1
484  *  %v = Volume name
485  *
486  *
487  *  omsg = edited output message
488  *  imsg = input string containing edit codes (%x)
489  *  cmd = command string (load, unload, ...)
490  *
491  */
492 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
493 {
494    const char *p;
495    const char *str;
496    char add[20];
497
498    *omsg = 0;
499    Dmsg1(1800, "edit_device_codes: %s\n", imsg);
500    for (p=imsg; *p; p++) {
501       if (*p == '%') {
502          switch (*++p) {
503          case '%':
504             str = "%";
505             break;
506          case 'a':
507             str = dcr->dev->archive_name();
508             break;
509          case 'c':
510             str = NPRT(dcr->device->changer_name);
511             break;
512          case 'd':
513             sprintf(add, "%d", dcr->dev->drive_index);
514             str = add;
515             break;
516          case 'o':
517             str = NPRT(cmd);
518             break;
519          case 's':
520             sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
521             str = add;
522             break;
523          case 'S':
524             sprintf(add, "%d", dcr->VolCatInfo.Slot);
525             str = add;
526             break;
527          case 'j':                    /* Job name */
528             str = dcr->jcr->Job;
529             break;
530          case 'v':
531             str = NPRT(dcr->VolumeName);
532             break;
533          case 'f':
534             str = NPRT(dcr->jcr->client_name);
535             break;
536
537          default:
538             add[0] = '%';
539             add[1] = *p;
540             add[2] = 0;
541             str = add;
542             break;
543          }
544       } else {
545          add[0] = *p;
546          add[1] = 0;
547          str = add;
548       }
549       Dmsg1(1900, "add_str %s\n", str);
550       pm_strcat(&omsg, (char *)str);
551       Dmsg1(1800, "omsg=%s\n", omsg);
552    }
553    Dmsg1(800, "omsg=%s\n", omsg);
554    return omsg;
555 }