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