]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/autochanger.c
kes Fix logic error in handling error return from mtx-changer
[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          dev->Slot = -1;           /* unknown */
336       } else {
337          dev->Slot = 0;            /* nothing loaded */
338       }
339       free_pool_memory(changer);
340       unlock_changer(dcr);
341    }
342    return ok;
343 }
344
345 /*
346  * Unload the slot if mounted in a different drive
347  */
348 static bool unload_other_drive(DCR *dcr, int slot)
349 {
350    DEVICE *dev = NULL;
351    DEVICE *save_dev;
352    JCR *jcr = dcr->jcr;
353    int save_slot;
354    uint32_t timeout = dcr->device->max_changer_wait;
355    bool ok = true;
356    AUTOCHANGER *changer = dcr->dev->device->changer_res;
357    DEVRES *device;
358    bool found = false;
359    bool first = true;
360
361    if (!changer) {
362       return false;
363    }
364    if (changer->device->size() == 1) {
365       return true;
366    }
367
368    foreach_alist(device, changer->device) {
369       if (device->dev && device->dev->Slot == slot) {
370          found = true;
371          dev = device->dev;
372          break;
373       }
374    }
375    if (!found) {
376       return true;
377    }
378
379    /* The Volume we want is on another device. */
380    if (dev->is_busy()) {
381       Dmsg4(100, "Vol %s for dev=%s in use dev=%s slot=%d\n",
382            dcr->VolumeName, dcr->dev->print_name(),
383            dev->print_name(), slot);
384    }   
385    for (int i=0; i < 3; i++) {
386       if (dev->is_busy()) {
387          wait_for_device(dcr->jcr, first);
388          first = false;
389          continue;
390       }
391       break;
392    }
393    P(dev->mutex);
394    if (dev->is_busy()) {
395       Jmsg(jcr, M_WARNING, 0, _("Volume \"%s\" is in use by device %s\n"),
396            dcr->VolumeName, dev->print_name());
397       Dmsg4(100, "Vol %s for dev=%s is busy dev=%s slot=%d\n",
398            dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), slot);
399       Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->reserved_device);
400       V(dev->mutex);
401       return false;
402    }
403
404    POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
405    lock_changer(dcr);
406    Jmsg(jcr, M_INFO, 0,
407         _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
408         slot, dev->drive_index);
409
410    Dmsg2(100, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
411         slot, dev->drive_index);
412
413    save_dev = dcr->dev;
414    dcr->dev = dev;
415    save_slot = dcr->VolCatInfo.Slot;
416    dcr->VolCatInfo.Slot = slot;
417    changer_cmd = edit_device_codes(dcr, changer_cmd, 
418                 dcr->device->changer_command, "unload");
419    dev->close();
420    Dmsg2(200, "close dev=%s reserve=%d\n", dev->print_name(), 
421       dev->reserved_device);
422    Dmsg1(100, "Run program=%s\n", changer_cmd);
423    int stat = run_program(changer_cmd, timeout, NULL);
424    dcr->VolCatInfo.Slot = save_slot;
425    dcr->dev = save_dev;
426    if (stat != 0) {
427       berrno be;
428       be.set_errno(stat);
429       Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
430               slot, dev->drive_index, be.strerror());
431
432       Dmsg3(100, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
433               slot, dev->drive_index, be.strerror());
434       ok = false;
435       dev->Slot = -1;          /* unknown */
436    } else {
437       dev->Slot = 0;           /* nothing loaded */
438       Dmsg0(100, "Slot unloaded\n");
439    }
440    unlock_changer(dcr);
441    V(dev->mutex);
442    free_pool_memory(changer_cmd);
443    return ok;
444 }
445
446
447
448 /*
449  * List the Volumes that are in the autoloader possibly
450  *   with their barcodes.
451  *   We assume that it is always the Console that is calling us.
452  */
453 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)  
454 {
455    DEVICE *dev = dcr->dev;
456    uint32_t timeout = dcr->device->max_changer_wait;
457    POOLMEM *changer;
458    BPIPE *bpipe;
459    int len = sizeof_pool_memory(dir->msg) - 1;
460    bool ok = false;
461    int stat;
462
463    if (!dev->is_autochanger() || !dcr->device->changer_name ||
464        !dcr->device->changer_command) {
465       if (strcmp(cmd, "drives") == 0) {
466          bnet_fsend(dir, "drives=1\n");
467       }
468       bnet_fsend(dir, _("3993 Device %s not an autochanger device.\n"),
469          dev->print_name());
470       return false;
471    }
472
473    if (strcmp(cmd, "drives") == 0) {
474       AUTOCHANGER *changer_res = dcr->device->changer_res;
475       int drives = 1;
476       if (changer_res) {
477          drives = changer_res->device->size();
478       }
479       bnet_fsend(dir, "drives=%d\n", drives);
480       Dmsg1(100, "drives=%d\n", drives);
481       return true;
482    }
483
484    changer = get_pool_memory(PM_FNAME);
485    lock_changer(dcr);
486    /* Now issue the command */
487    changer = edit_device_codes(dcr, changer, 
488                  dcr->device->changer_command, cmd);
489    bnet_fsend(dir, _("3306 Issuing autochanger \"%s\" command.\n"), cmd);
490    bpipe = open_bpipe(changer, timeout, "r");
491    if (!bpipe) {
492       bnet_fsend(dir, _("3996 Open bpipe failed.\n"));
493       goto bail_out;
494    }
495    if (strcmp(cmd, "list") == 0) {
496       /* Get output from changer */
497       while (fgets(dir->msg, len, bpipe->rfd)) {
498          dir->msglen = strlen(dir->msg);
499          Dmsg1(100, "<stored: %s\n", dir->msg);
500          bnet_send(dir);
501       }
502    } else if (strcmp(cmd, "slots") == 0 ) {
503       char buf[100], *p;
504       /* For slots command, read a single line */
505       buf[0] = 0;
506       fgets(buf, sizeof(buf)-1, bpipe->rfd);
507       buf[sizeof(buf)-1] = 0;
508       /* Strip any leading space in front of # of slots */
509       for (p=buf; B_ISSPACE(*p); p++)
510         { }
511       bnet_fsend(dir, "slots=%s", p);
512       Dmsg1(100, "<stored: %s", dir->msg);
513    } 
514                  
515    stat = close_bpipe(bpipe);
516    if (stat != 0) {
517       berrno be;
518       be.set_errno(stat);
519       bnet_fsend(dir, _("Autochanger error: ERR=%s\n"), be.strerror());
520    }
521    bnet_sig(dir, BNET_EOD);
522    ok = true;
523
524 bail_out:
525    unlock_changer(dcr);
526    free_pool_memory(changer);
527    return true;
528 }
529
530
531 /*
532  * Edit codes into ChangerCommand
533  *  %% = %
534  *  %a = archive device name
535  *  %c = changer device name
536  *  %d = changer drive index
537  *  %f = Client's name
538  *  %j = Job name
539  *  %o = command
540  *  %s = Slot base 0
541  *  %S = Slot base 1
542  *  %v = Volume name
543  *
544  *
545  *  omsg = edited output message
546  *  imsg = input string containing edit codes (%x)
547  *  cmd = command string (load, unload, ...)
548  *
549  */
550 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
551 {
552    const char *p;
553    const char *str;
554    char add[20];
555
556    *omsg = 0;
557    Dmsg1(1800, "edit_device_codes: %s\n", imsg);
558    for (p=imsg; *p; p++) {
559       if (*p == '%') {
560          switch (*++p) {
561          case '%':
562             str = "%";
563             break;
564          case 'a':
565             str = dcr->dev->archive_name();
566             break;
567          case 'c':
568             str = NPRT(dcr->device->changer_name);
569             break;
570          case 'd':
571             sprintf(add, "%d", dcr->dev->drive_index);
572             str = add;
573             break;
574          case 'o':
575             str = NPRT(cmd);
576             break;
577          case 's':
578             sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
579             str = add;
580             break;
581          case 'S':
582             sprintf(add, "%d", dcr->VolCatInfo.Slot);
583             str = add;
584             break;
585          case 'j':                    /* Job name */
586             str = dcr->jcr->Job;
587             break;
588          case 'v':
589             str = NPRT(dcr->VolumeName);
590             break;
591          case 'f':
592             str = NPRT(dcr->jcr->client_name);
593             break;
594
595          default:
596             add[0] = '%';
597             add[1] = *p;
598             add[2] = 0;
599             str = add;
600             break;
601          }
602       } else {
603          add[0] = *p;
604          add[1] = 0;
605          str = add;
606       }
607       Dmsg1(1900, "add_str %s\n", str);
608       pm_strcat(&omsg, (char *)str);
609       Dmsg1(1800, "omsg=%s\n", omsg);
610    }
611    Dmsg1(800, "omsg=%s\n", omsg);
612    return omsg;
613 }