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