]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/mount.c
Autochanger fixes
[bacula/bacula] / bacula / src / stored / mount.c
1 /*
2  *
3  *  Routines for handling mounting tapes for reading and for
4  *    writing.
5  *
6  *   Kern Sibbald, August MMII
7  *                            
8  *   Version $Id$
9  */
10 /*
11    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
12
13    This program is free software; you can redistribute it and/or
14    modify it under the terms of the GNU General Public License as
15    published by the Free Software Foundation; either version 2 of
16    the License, or (at your option) any later version.
17
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21    General Public License for more details.
22
23    You should have received a copy of the GNU General Public
24    License along with this program; if not, write to the Free
25    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
26    MA 02111-1307, USA.
27
28  */
29
30 #include "bacula.h"                   /* pull in global headers */
31 #include "stored.h"                   /* pull in Storage Deamon headers */
32
33 /* Forward referenced functions */
34 static char *edit_device_codes(JCR *jcr, char *omsg, char *imsg, char *cmd);
35
36
37 /*
38  * If release is set, we rewind the current volume, 
39  * which we no longer want, and ask the user (console) 
40  * to mount the next volume.
41  *
42  *  Continue trying until we get it, and then ensure
43  *  that we can write on it.
44  *
45  * This routine returns a 0 only if it is REALLY
46  *  impossible to get the requested Volume.
47  */
48 int mount_next_write_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, int release)
49 {
50    int recycle, ask, retry = 0, autochanger;
51
52    Dmsg0(190, "Enter mount_next_volume()\n");
53
54 mount_next_vol:
55    if (retry++ > 5) {
56       Jmsg(jcr, M_FATAL, 0, _("Too many errors on device %s.\n"), dev_name(dev));
57       return 0;
58    }
59    if (job_cancelled(jcr)) {
60       Jmsg(jcr, M_FATAL, 0, _("Job cancelled.\n"));
61       return 0;
62    }
63    recycle = ask = autochanger = 0;
64    if (release) {
65       Dmsg0(500, "mount_next_volume release=1\n");
66       /* 
67        * First erase all memory of the current volume   
68        */
69       dev->block_num = 0;
70       dev->file = 0;
71       dev->LastBlockNumWritten = 0;
72       memset(&dev->VolCatInfo, 0, sizeof(dev->VolCatInfo));
73       memset(&dev->VolHdr, 0, sizeof(dev->VolHdr));
74       dev->state &= ~ST_LABEL;        /* label not yet read */
75       jcr->VolumeName[0] = 0;
76
77       if (!dev_is_tape(dev) || !(dev->capabilities & CAP_ALWAYSOPEN)) {
78          if (dev->capabilities & CAP_OFFLINEUNMOUNT) {
79             offline_dev(dev);
80          }
81          close_dev(dev);
82       }
83
84       /* If we have not closed the device, then at least rewind the tape */
85       if (dev->state & ST_OPENED) {
86          if (dev->capabilities & CAP_OFFLINEUNMOUNT) {
87             offline_dev(dev);
88          }
89          if (!rewind_dev(dev)) {
90             Jmsg2(jcr, M_WARNING, 0, _("Rewind error on device %s. ERR=%s\n"), 
91                   dev_name(dev), strerror_dev(dev));
92          }
93       }
94       ask = 1;                        /* ask operator to mount tape */
95    }
96
97    /* 
98     * Get Director's idea of what tape we should have mounted. 
99     */
100    if (!dir_find_next_appendable_volume(jcr)) {
101       ask = 1;                     /* we must ask */
102    }
103    Dmsg2(100, "After find_next_append. Vol=%s Slot=%d\n",
104       jcr->VolCatInfo.VolCatName, jcr->VolCatInfo.Slot);
105    release = 1;                       /* release if we "recurse" */
106
107    /* 
108     * Get next volume and ready it for append
109     * This code ensures that the device is ready for
110     * writing. We start from the assumption that there
111     * may not be a tape mounted. 
112     *
113     * If the device is a file, we create the output
114     * file. If it is a tape, we check the volume name
115     * and move the tape to the end of data.
116     *
117     * It assumes that the device is not already in use!
118     *
119     */
120
121    Dmsg0(100, "Enter ready_dev_for_append\n");
122
123    dev->state &= ~(ST_APPEND|ST_READ|ST_EOT|ST_WEOT|ST_EOF);
124
125    jcr->VolFirstFile = jcr->JobFiles; /* first update of Vol FileIndex */
126    for ( ;; ) {
127       int slot = jcr->VolCatInfo.Slot;
128         
129       /*
130        * Handle autoloaders here.  If we cannot autoload it, we
131        *  will fall through to ask the sysop.
132        */
133       if (dev->capabilities && CAP_AUTOCHANGER && slot <= 0) {
134          if (dir_find_next_appendable_volume(jcr)) {
135             slot = jcr->VolCatInfo.Slot; 
136          }
137       }
138       Dmsg1(100, "Want changer slot=%d\n", slot);
139
140       if (slot > 0 && jcr->device->changer_name && jcr->device->changer_command) {
141          uint32_t timeout = jcr->device->max_changer_wait;
142          POOLMEM *changer, *results;
143          int status, loaded;
144
145          results = get_pool_memory(PM_MESSAGE);
146          changer = get_pool_memory(PM_FNAME);
147          /* Find out what is loaded */
148          changer = edit_device_codes(jcr, changer, jcr->device->changer_command, 
149                       "loaded");
150          status = run_program(changer, timeout, results);
151          if (status == 0) {
152             loaded = atoi(results);
153          } else {
154             loaded = -1;              /* force unload */
155          }
156          Dmsg1(100, "loaded=%s\n", results);
157
158          /* If bad status or tape we want is not loaded, load it. */
159          if (status != 0 || loaded != slot) { 
160             if (dev->capabilities & CAP_OFFLINEUNMOUNT) {
161                offline_dev(dev);
162             }
163             /* We are going to load a new tape, so close the device */
164             force_close_dev(dev);
165             if (loaded != 0) {        /* must unload drive */
166                Dmsg0(100, "Doing changer unload.\n");
167                Jmsg(jcr, M_INFO, 0, _("Issuing autochanger \"unload\" command.\n"));
168                changer = edit_device_codes(jcr, changer, 
169                            jcr->device->changer_command, "unload");
170                status = run_program(changer, timeout, NULL);
171                Dmsg1(100, "unload status=%d\n", status);
172             }
173             /*
174              * Load the desired cassette    
175              */
176             Dmsg1(100, "Doing changer load slot %d\n", slot);
177             Jmsg(jcr, M_INFO, 0, _("Issuing autochanger \"load slot %d\" command.\n"),
178                slot);
179             changer = edit_device_codes(jcr, changer, 
180                          jcr->device->changer_command, "load");
181             status = run_program(changer, timeout, NULL);
182             Dmsg2(100, "load slot %d status=%d\n", slot, status);
183          }
184          free_pool_memory(changer);
185          free_pool_memory(results);
186          Dmsg1(100, "After changer, status=%d\n", status);
187          if (status == 0) {           /* did we succeed? */
188             ask = 0;                  /* yes, got vol, no need to ask sysop */
189             autochanger = 1;          /* tape loaded by changer */
190          }
191       }
192
193
194       if (ask && !dir_ask_sysop_to_mount_next_volume(jcr, dev)) {
195          return 0;              /* error return */
196       }
197       Dmsg1(200, "want vol=%s\n", jcr->VolumeName);
198
199       /* Open device */
200       for ( ; !(dev->state & ST_OPENED); ) {
201           if (open_dev(dev, jcr->VolCatInfo.VolCatName, READ_WRITE) < 0) {
202              if (dev->dev_errno == EAGAIN || dev->dev_errno == EBUSY) {
203                 sleep(30);
204              }
205              Jmsg2(jcr, M_FATAL, 0, _("Unable to open device %s. ERR=%s\n"), 
206                 dev_name(dev), strerror_dev(dev));
207              return 0;
208           }
209       }
210
211       /*
212        * Now make sure we have the right tape mounted
213        */
214 read_volume:
215       switch (read_dev_volume_label(jcr, dev, block)) {
216          case VOL_OK:
217             Dmsg1(500, "Vol OK name=%s\n", jcr->VolumeName);
218             memcpy(&dev->VolCatInfo, &jcr->VolCatInfo, sizeof(jcr->VolCatInfo));
219             if (strcmp(dev->VolCatInfo.VolCatStatus, "Recycle") == 0) {
220                recycle = 1;
221             }
222             break;                    /* got it */
223          case VOL_NAME_ERROR:
224             Dmsg1(500, "Vol NAME Error Name=%s\n", jcr->VolumeName);
225             /* Check if we can accept this as an anonymous volume */
226             strcpy(jcr->VolumeName, dev->VolHdr.VolName);
227             if (!dev->capabilities & CAP_ANONVOLS || !dir_get_volume_info(jcr)) {
228                goto mount_next_vol;
229             }
230             Dmsg1(200, "want new name=%s\n", jcr->VolumeName);
231             memcpy(&dev->VolCatInfo, &jcr->VolCatInfo, sizeof(jcr->VolCatInfo));
232             break;
233
234          case VOL_NO_LABEL:
235          case VOL_IO_ERROR:
236             Dmsg1(500, "Vol NO_LABEL or IO_ERROR name=%s\n", jcr->VolumeName);
237             /* If permitted, create a label */
238             if (dev->capabilities & CAP_LABEL) {
239                Dmsg0(190, "Create volume label\n");
240                if (!write_volume_label_to_dev(jcr, (DEVRES *)dev->device, jcr->VolumeName,
241                       jcr->pool_name)) {
242                   goto mount_next_vol;
243                }
244                Jmsg(jcr, M_INFO, 0, _("Created Volume label %s on device %s.\n"),
245                   jcr->VolumeName, dev_name(dev));
246                goto read_volume;      /* read label we just wrote */
247             } 
248             /* NOTE! Fall-through wanted. */
249          default:
250             /* Send error message */
251             Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);                         
252             if (autochanger) {
253                Jmsg(jcr, M_ERROR, 0, _("Autochanger Volume %s not found in slot %d.\n\
254     Setting slot to zero in catalog.\n"),
255                   jcr->VolumeName, jcr->VolCatInfo.Slot);
256                jcr->VolCatInfo.Slot = 0; /* invalidate slot */
257                dir_update_volume_info(jcr, &jcr->VolCatInfo, 1);  /* set slot */
258             }
259             goto mount_next_vol;
260       }
261       break;
262    }
263
264    /* 
265     * See if we have a fresh tape or tape with data.
266     *
267     * Note, if the LabelType is PRE_LABEL, it was labeled
268     *  but never written. If so, rewrite the label but set as
269     *  VOL_LABEL.  We rewind and return the label (reconstructed)
270     *  in the block so that in the case of a new tape, data can
271     *  be appended just after the block label.  If we are writing
272     *  an second volume, the calling routine will write the label
273     *  before writing the overflow block.
274     *
275     *  If the tape is marked as Recycle, we rewrite the label.
276     */
277    if (dev->VolHdr.LabelType == PRE_LABEL || recycle) {
278       Dmsg1(190, "ready_for_append found freshly labeled volume. dev=%x\n", dev);
279       dev->VolHdr.LabelType = VOL_LABEL; /* set Volume label */
280       write_volume_label_to_block(jcr, dev, block);
281       /*
282        * Write the block now to ensure we have write permission.
283        *  It is better to find out now rather than later.
284        */
285       dev->VolCatInfo.VolCatBytes = 0;
286       if (!rewind_dev(dev)) {
287          Jmsg2(jcr, M_WARNING, 0, _("Rewind error on device %s. ERR=%s\n"), 
288                dev_name(dev), strerror_dev(dev));
289       }
290       if (recycle) {
291          if (!truncate_dev(dev)) {
292             Jmsg2(jcr, M_WARNING, 0, _("Truncate error on device %s. ERR=%s\n"), 
293                   dev_name(dev), strerror_dev(dev));
294          }
295       }
296       if (!write_block_to_dev(dev, block)) {
297          Jmsg2(jcr, M_ERROR, 0, _("Unable to write device %s. ERR=%s\n"),
298             dev_name(dev), strerror_dev(dev));
299          goto mount_next_vol;
300       }
301       if (!rewind_dev(dev)) {
302          Jmsg2(jcr, M_ERROR, 0, _("Unable to rewind device %s. ERR=%s\n"),
303             dev_name(dev), strerror_dev(dev));
304          goto mount_next_vol;
305       }
306       /* Recreate a correct volume label and return it in the block */
307       write_volume_label_to_block(jcr, dev, block);
308       dev->VolCatInfo.VolCatJobs = 1;
309       dev->VolCatInfo.VolCatFiles = 1;
310       dev->VolCatInfo.VolCatErrors = 0;
311       dev->VolCatInfo.VolCatBlocks = 1;
312       if (recycle) {
313          dev->VolCatInfo.VolCatMounts++;  
314          dev->VolCatInfo.VolCatRecycles++;
315       } else {
316          dev->VolCatInfo.VolCatMounts = 1;
317          dev->VolCatInfo.VolCatRecycles = 0;
318          dev->VolCatInfo.VolCatWrites = 1;
319          dev->VolCatInfo.VolCatReads = 1;
320       }
321       strcpy(dev->VolCatInfo.VolCatStatus, "Append");
322       Dmsg0(100, "dir_update_vol_info. Set Append\n");
323       dir_update_volume_info(jcr, &dev->VolCatInfo, 1);  /* indicate doing relabel */
324       if (recycle) {
325          Jmsg(jcr, M_INFO, 0, _("Recycled volume %s on device %s, all previous data lost.\n"),
326             jcr->VolumeName, dev_name(dev));
327       } else {
328          Jmsg(jcr, M_INFO, 0, _("Wrote label to prelabeled Volume %s on device %s\n"),
329             jcr->VolumeName, dev_name(dev));
330       }
331
332    } else {
333       /*
334        * OK, at this point, we have a valid Bacula label, but
335        * we need to position to the end of the volume, since we are
336        * just now putting it into append mode.
337        */
338       Dmsg0(200, "Device previously written, moving to end of data\n");
339       Jmsg(jcr, M_INFO, 0, _("Volume %s previously written, moving to end of data.\n"),
340          jcr->VolumeName);
341       if (!eod_dev(dev)) {
342          Jmsg(jcr, M_ERROR, 0, _("Unable to position to end of data %s. ERR=%s\n"),
343             dev_name(dev), strerror_dev(dev));
344          Jmsg(jcr, M_INFO, 0, _("Marking Volume %s in Error in Catalog.\n"),
345             jcr->VolumeName);
346          strcpy(dev->VolCatInfo.VolCatStatus, "Error");
347          Dmsg0(100, "dir_update_vol_info. Set Error.\n");
348          dir_update_volume_info(jcr, &dev->VolCatInfo, 0);
349          goto mount_next_vol;
350       }
351       /* *****FIXME**** we should do some checking for files too */
352       if (dev_is_tape(dev)) {
353          Jmsg(jcr, M_INFO, 0, _("Ready to append to end of Volume at file=%d.\n"), dev_file(dev));
354          /*
355           * Check if we are positioned on the tape at the same place
356           * that the database says we should be.
357           */
358          if (dev->VolCatInfo.VolCatFiles != dev_file(dev) + 1) {
359             /* ****FIXME**** this should refuse to write on tape */
360             Jmsg(jcr, M_ERROR, 0, _("Hey! Num files mismatch! Volume=%d Catalog=%d\n"), 
361                dev_file(dev)+1, dev->VolCatInfo.VolCatFiles);
362          }
363       }
364       /* Update Volume Info -- will be written at end of Job */
365       dev->VolCatInfo.VolCatMounts++;      /* Update mounts */
366       dev->VolCatInfo.VolCatJobs++;
367       /* Return an empty block */
368       empty_block(block);             /* we used it for reading so set for write */
369    }
370    dev->state |= ST_APPEND;
371    Dmsg0(100, "Normal return from read_dev_for_append\n");
372    return 1; 
373 }
374
375
376 int mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
377 {
378    Dmsg2(90, "NumVolumes=%d CurVolume=%d\n", jcr->NumVolumes, jcr->CurVolume);
379    /*
380     * End Of Tape -- mount next Volume (if another specified)
381     */
382    if (jcr->NumVolumes > 1 && jcr->CurVolume < jcr->NumVolumes) {
383       VOL_LIST *vol = jcr->VolList;
384       /* Find next Volume */
385       jcr->CurVolume++;
386       for (int i=1; i<jcr->CurVolume; i++) {
387          vol = vol->next;
388       }
389       pm_strcpy(&jcr->VolumeName, vol->VolumeName);
390       Dmsg1(400, "There is another volume %s.\n", jcr->VolumeName);
391
392       close_dev(dev);
393       dev->state &= ~ST_READ; 
394       if (!acquire_device_for_read(jcr, dev, block)) {
395          Emsg2(M_FATAL, 0, "Cannot open Dev=%s, Vol=%s\n", dev_name(dev),
396                jcr->VolumeName);
397          return 0;
398       }
399       return 1;                       /* next volume mounted */
400    }
401    Dmsg0(90, "End of Device reached.\n");
402    return 0;
403 }
404
405
406 /*
407  * Edit codes into ChangerCommand
408  *  %% = %
409  *  %a = archive device name
410  *  %c = changer device name
411  *  %f = Client's name
412  *  %j = Job name
413  *  %o = command
414  *  %s = Slot base 0
415  *  %S = Slot base 1
416  *  %v = Volume name
417  *
418  *
419  *  omsg = edited output message
420  *  imsg = input string containing edit codes (%x)
421  *  cmd = command string (load, unload, ...) 
422  *
423  */
424 static char *edit_device_codes(JCR *jcr, char *omsg, char *imsg, char *cmd) 
425 {
426    char *p;
427    const char *str;
428    char add[20];
429
430    *omsg = 0;
431    Dmsg1(200, "edit_device_codes: %s\n", imsg);
432    for (p=imsg; *p; p++) {
433       if (*p == '%') {
434          switch (*++p) {
435          case '%':
436             str = "%";
437             break;
438          case 'a':
439             str = jcr->device->dev->dev_name;
440             break;
441          case 'c':
442             str = NPRT(jcr->device->changer_name);
443             break;
444          case 'o':
445             str = NPRT(cmd);
446             break;
447          case 's':
448             sprintf(add, "%d", jcr->VolCatInfo.Slot - 1);
449             str = add;
450             break;
451          case 'S':
452             sprintf(add, "%d", jcr->VolCatInfo.Slot);
453             str = add;
454             break;
455          case 'j':                    /* Job name */
456             str = jcr->Job;
457             break;
458          case 'v':
459             str = NPRT(jcr->VolumeName);
460             break;
461          case 'f':
462             str = NPRT(jcr->client_name);
463             break;
464
465          default:
466             add[0] = '%';
467             add[1] = *p;
468             add[2] = 0;
469             str = add;
470             break;
471          }
472       } else {
473          add[0] = *p;
474          add[1] = 0;
475          str = add;
476       }
477       Dmsg1(200, "add_str %s\n", str);
478       pm_strcat(&omsg, (char *)str);
479       Dmsg1(200, "omsg=%s\n", omsg);
480    }
481    return omsg;
482 }