]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/autochanger.c
dvd.c:dvd_write_part: Increase timeout when writing the first part (see the code...
[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 /*
33  * Called here to do an autoload using the autochanger, if
34  *  configured, and if a Slot has been defined for this Volume.
35  *  On success this routine loads the indicated tape, but the
36  *  label is not read, so it must be verified.
37  *
38  *  Note if dir is not NULL, it is the console requesting the
39  *   autoload for labeling, so we respond directly to the
40  *   dir bsock.
41  *
42  *  Returns: 1 on success
43  *           0 on failure (no changer available)
44  *          -1 on error on autochanger
45  */
46 int autoload_device(DCR *dcr, int writing, BSOCK *dir)
47 {
48    JCR *jcr = dcr->jcr;
49    DEVICE *dev = dcr->dev;
50    int slot;
51    int drive = dev->drive_index;
52    int rtn_stat = -1;                 /* error status */
53    POOLMEM *changer;
54
55    slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
56    /*
57     * Handle autoloaders here.  If we cannot autoload it, we
58     *  will return 0 so that the sysop will be asked to load it.
59     */
60    if (writing && dev->is_autochanger() && slot <= 0) {
61       if (dir) {
62          return 0;                    /* For user, bail out right now */
63       }
64       if (dir_find_next_appendable_volume(dcr)) {
65          slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
66       } else {
67          slot = 0;
68       }
69    }
70    Dmsg1(400, "Want changer slot=%d\n", slot);
71
72    changer = get_pool_memory(PM_FNAME);
73    if (slot > 0 && dcr->device->changer_name && dcr->device->changer_command) {
74       uint32_t timeout = dcr->device->max_changer_wait;
75       int loaded, status;
76
77       loaded = get_autochanger_loaded_slot(dcr);
78
79       if (loaded != slot) {
80          /* Unload anything in our drive */
81          if (!unload_autochanger(dcr, loaded)) {
82             goto bail_out;
83          }
84             
85          /* Make sure desired slot is unloaded */
86          if (!unload_other_drive(dcr, slot)) {
87             goto bail_out;
88          }
89
90          /* We are going to load a new tape, so close the device */
91          offline_or_rewind_dev(dev);
92          force_close_device(dev);
93
94          /*
95           * Load the desired cassette
96           */
97          Dmsg1(400, "Doing changer load slot %d\n", slot);
98          Jmsg(jcr, M_INFO, 0,
99               _("3304 Issuing autochanger \"load slot %d, drive %d\" command.\n"),
100               slot, drive);
101          dcr->VolCatInfo.Slot = slot;    /* slot to be loaded */
102          changer = edit_device_codes(dcr, changer, 
103                       dcr->device->changer_command, "load");
104          status = run_program(changer, timeout, NULL);
105          if (status == 0) {
106             Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"),
107                     slot, drive);
108             dev->Slot = slot;         /* set currently loaded slot */
109          } else {
110            berrno be;
111            be.set_errno(status);
112             Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": ERR=%s.\n"),
113                     slot, drive, be.strerror());
114             goto bail_out;
115          }
116          unlock_changer(dcr);
117          Dmsg2(400, "load slot %d status=%d\n", slot, status);
118       } else {
119          status = 0;                  /* we got what we want */
120          dev->Slot = slot;            /* set currently loaded slot */
121       }
122       Dmsg1(400, "After changer, status=%d\n", status);
123       if (status == 0) {              /* did we succeed? */
124          rtn_stat = 1;                /* tape loaded by changer */
125       }
126    } else {
127       rtn_stat = 0;                   /* no changer found */
128    }
129    free_pool_memory(changer);
130    return rtn_stat;
131
132 bail_out:
133    free_pool_memory(changer);
134    unlock_changer(dcr);
135    return -1;
136
137 }
138
139 /*
140  * Returns: -1 if error from changer command
141  *          slot otherwise
142  */
143 int get_autochanger_loaded_slot(DCR *dcr)
144 {
145    JCR *jcr = dcr->jcr;
146    POOLMEM *changer, *results;
147    int status, loaded;
148    uint32_t timeout = dcr->device->max_changer_wait;
149    int drive = dcr->dev->drive_index;
150
151    results = get_pool_memory(PM_MESSAGE);
152    changer = get_pool_memory(PM_FNAME);
153
154    lock_changer(dcr);
155
156    /* Find out what is loaded, zero means device is unloaded */
157    Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded drive %d\" command.\n"),
158         drive);
159    changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded");
160    *results = 0;
161    status = run_program(changer, timeout, results);
162    Dmsg3(50, "run_prog: %s stat=%d result=%s\n", changer, status, results);
163    if (status == 0) {
164       loaded = atoi(results);
165       if (loaded > 0) {
166          Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result is Slot %d.\n"),
167               drive, loaded);
168          dcr->dev->Slot = loaded;
169       } else {
170          Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result: nothing loaded.\n"),
171               drive);
172          dcr->dev->Slot = 0;
173       }
174    } else {
175       berrno be;
176       be.set_errno(status);
177       Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded drive %d\" command: ERR=%s.\n"),
178            drive, be.strerror());
179       loaded = -1;              /* force unload */
180    }
181    unlock_changer(dcr);
182    free_pool_memory(changer);
183    free_pool_memory(results);
184    return loaded;
185 }
186
187 static void lock_changer(DCR *dcr)
188 {
189    AUTOCHANGER *changer_res = dcr->device->changer_res;
190    if (changer_res) {
191       Dmsg1(100, "Locking changer %s\n", changer_res->hdr.name);
192       P(changer_res->changer_mutex);  /* Lock changer script */
193    }
194 }
195
196 static void unlock_changer(DCR *dcr)
197 {
198    AUTOCHANGER *changer_res = dcr->device->changer_res;
199    if (changer_res) {
200       Dmsg1(100, "Unlocking changer %s\n", changer_res->hdr.name);
201       V(changer_res->changer_mutex);  /* Unlock changer script */
202    }
203 }
204
205 /*
206  * Unload the volume, if any, in this drive
207  *  On entry: loaded == 0 -- nothing to do
208  *            loaded  < 0 -- check if anything to do
209  *            loaded  > 0 -- load slot == loaded
210  */
211 bool unload_autochanger(DCR *dcr, int loaded)
212 {
213    DEVICE *dev = dcr->dev;
214    JCR *jcr = dcr->jcr;
215    int slot;
216    uint32_t timeout = dcr->device->max_changer_wait;
217    bool ok = true;
218
219    if (loaded == 0) {
220       return true;
221    }
222
223    if (!dev->is_autochanger() || !dcr->device->changer_name ||
224        !dcr->device->changer_command) {
225       return false;
226    }
227
228    /* We are going to load a new tape, so close the device */
229    offline_or_rewind_dev(dev);
230    force_close_device(dev);
231
232    if (loaded < 0) {
233       loaded = get_autochanger_loaded_slot(dcr);
234    }
235    if (loaded > 0) {
236       POOLMEM *changer = get_pool_memory(PM_FNAME);
237       Jmsg(jcr, M_INFO, 0,
238            _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
239            loaded, dev->drive_index);
240       slot = dcr->VolCatInfo.Slot;
241       dcr->VolCatInfo.Slot = loaded;
242       changer = edit_device_codes(dcr, changer, 
243                    dcr->device->changer_command, "unload");
244       lock_changer(dcr);
245       int stat = run_program(changer, timeout, NULL);
246       unlock_changer(dcr);
247       dcr->VolCatInfo.Slot = slot;
248       if (stat != 0) {
249          berrno be;
250          be.set_errno(stat);
251          Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
252                  slot, dev->drive_index, be.strerror());
253          ok = false;
254       } else {
255          dev->Slot = 0;            /* nothing loaded */
256       }
257       free_pool_memory(changer);
258    }
259    return ok;
260 }
261
262 /*
263  * Unload the slot if mounted in a different drive
264  */
265 static bool unload_other_drive(DCR *dcr, int slot)
266 {
267    DEVICE *dev = NULL;
268    DEVICE *save_dev;
269    JCR *jcr = dcr->jcr;
270    int save_slot;
271    uint32_t timeout = dcr->device->max_changer_wait;
272    bool ok = true;
273    AUTOCHANGER *changer = dcr->dev->device->changer_res;
274    DEVRES *device;
275    bool found = false;
276
277    if (!changer) {
278       return false;
279    }
280    if (changer->device->size() == 1) {
281       return true;
282    }
283
284    foreach_alist(device, changer->device) {
285       if (device->dev && device->dev->Slot == slot) {
286          found = true;
287          dev = device->dev;
288          break;
289       }
290    }
291    if (!found) {
292       return true;
293    }
294    if (dev->is_busy()) {
295       Jmsg(jcr, M_WARNING, 0, _("Volume %s is in use by device %s\n"),
296            dcr->VolumeName, dev->print_name());
297       Dmsg2(200, "Volume %s is in use by device %s\n",
298            dcr->VolumeName, dev->print_name());
299       
300       return false;
301    }
302
303    /* We are going to unload a tape, so close the device */
304    offline_or_rewind_dev(dev);
305    force_close_device(dev);
306
307    POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
308    Jmsg(jcr, M_INFO, 0,
309         _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
310         slot, dev->drive_index);
311
312    Dmsg2(200, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
313         slot, dev->drive_index);
314
315    save_slot = dcr->VolCatInfo.Slot;
316    save_dev = dcr->dev;
317    dcr->dev = dev;
318    dcr->VolCatInfo.Slot = slot;
319    changer_cmd = edit_device_codes(dcr, changer_cmd, 
320                 dcr->device->changer_command, "unload");
321    lock_changer(dcr);
322    Dmsg1(200, "Run program=%s\n", changer_cmd);
323    int stat = run_program(changer_cmd, timeout, NULL);
324    unlock_changer(dcr);
325    dcr->VolCatInfo.Slot = save_slot;
326    dcr->dev = save_dev;
327    if (stat != 0) {
328       berrno be;
329       be.set_errno(stat);
330       Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
331               slot, dev->drive_index, be.strerror());
332
333       Dmsg3(200, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
334               slot, dev->drive_index, be.strerror());
335       ok = false;
336    } else {
337       dev->Slot = 0;            /* nothing loaded */
338       Dmsg0(200, "Slot unloaded\n");
339    }
340    free_pool_memory(changer_cmd);
341    return ok;
342 }
343
344
345
346 /*
347  * List the Volumes that are in the autoloader possibly
348  *   with their barcodes.
349  *   We assume that it is always the Console that is calling us.
350  */
351 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)  
352 {
353    DEVICE *dev = dcr->dev;
354    uint32_t timeout = dcr->device->max_changer_wait;
355    POOLMEM *changer;
356    BPIPE *bpipe;
357    int len = sizeof_pool_memory(dir->msg) - 1;
358    bool ok = false;
359    int stat;
360
361    if (!dev->is_autochanger() || !dcr->device->changer_name ||
362        !dcr->device->changer_command) {
363       bnet_fsend(dir, _("3993 Device %s not an autochanger device.\n"),
364          dev->print_name());
365       return false;
366    }
367
368    changer = get_pool_memory(PM_FNAME);
369    /* List command? */
370    if (strcmp(cmd, "list") == 0) {
371       unload_autochanger(dcr, -1);
372    }
373
374    /* Now issue the command */
375    changer = edit_device_codes(dcr, changer, 
376                  dcr->device->changer_command, cmd);
377    bnet_fsend(dir, _("3306 Issuing autochanger \"%s\" command.\n"), cmd);
378    lock_changer(dcr);
379    bpipe = open_bpipe(changer, timeout, "r");
380    if (!bpipe) {
381       unlock_changer(dcr);
382       bnet_fsend(dir, _("3996 Open bpipe failed.\n"));
383       goto bail_out;
384    }
385    if (strcmp(cmd, "list") == 0) {
386       /* Get output from changer */
387       while (fgets(dir->msg, len, bpipe->rfd)) {
388          dir->msglen = strlen(dir->msg);
389          Dmsg1(100, "<stored: %s\n", dir->msg);
390          bnet_send(dir);
391       }
392    } else {
393       /* For slots command, read a single line */
394       bstrncpy(dir->msg, "slots=", len);
395       fgets(dir->msg+6, len-6, bpipe->rfd);
396       dir->msglen = strlen(dir->msg);
397       Dmsg1(100, "<stored: %s", dir->msg);
398       bnet_send(dir);
399    }
400                  
401    stat = close_bpipe(bpipe);
402    unlock_changer(dcr);
403    if (stat != 0) {
404       berrno be;
405       be.set_errno(stat);
406       bnet_fsend(dir, _("Autochanger error: ERR=%s\n"), be.strerror());
407    }
408    bnet_sig(dir, BNET_EOD);
409    ok = true;
410
411 bail_out:
412    free_pool_memory(changer);
413    return true;
414 }
415
416
417 /*
418  * Edit codes into ChangerCommand
419  *  %% = %
420  *  %a = archive device name
421  *  %c = changer device name
422  *  %d = changer drive index
423  *  %f = Client's name
424  *  %j = Job name
425  *  %o = command
426  *  %s = Slot base 0
427  *  %S = Slot base 1
428  *  %v = Volume name
429  *
430  *
431  *  omsg = edited output message
432  *  imsg = input string containing edit codes (%x)
433  *  cmd = command string (load, unload, ...)
434  *
435  */
436 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
437 {
438    const char *p;
439    const char *str;
440    char add[20];
441
442    *omsg = 0;
443    Dmsg1(1800, "edit_device_codes: %s\n", imsg);
444    for (p=imsg; *p; p++) {
445       if (*p == '%') {
446          switch (*++p) {
447          case '%':
448             str = "%";
449             break;
450          case 'a':
451             str = dcr->dev->archive_name();
452             break;
453          case 'c':
454             str = NPRT(dcr->device->changer_name);
455             break;
456          case 'd':
457             sprintf(add, "%d", dcr->dev->drive_index);
458             str = add;
459             break;
460          case 'o':
461             str = NPRT(cmd);
462             break;
463          case 's':
464             sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
465             str = add;
466             break;
467          case 'S':
468             sprintf(add, "%d", dcr->VolCatInfo.Slot);
469             str = add;
470             break;
471          case 'j':                    /* Job name */
472             str = dcr->jcr->Job;
473             break;
474          case 'v':
475             str = NPRT(dcr->VolumeName);
476             break;
477          case 'f':
478             str = NPRT(dcr->jcr->client_name);
479             break;
480
481          default:
482             add[0] = '%';
483             add[1] = *p;
484             add[2] = 0;
485             str = add;
486             break;
487          }
488       } else {
489          add[0] = *p;
490          add[1] = 0;
491          str = add;
492       }
493       Dmsg1(1900, "add_str %s\n", str);
494       pm_strcat(&omsg, (char *)str);
495       Dmsg1(1800, "omsg=%s\n", omsg);
496    }
497    Dmsg1(800, "omsg=%s\n", omsg);
498    return omsg;
499 }