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