]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/mount.c
aac5c57d5b48311a725b076050ec47715ac77371
[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       autochanger = autoload_device(jcr, dev, 1);
128       if (autochanger) {
129          ask = 0;                     /* if autochange no need to ask sysop */
130       }
131
132       if (ask && !dir_ask_sysop_to_mount_next_volume(jcr, dev)) {
133          return 0;              /* error return */
134       }
135       Dmsg1(200, "want vol=%s\n", jcr->VolumeName);
136
137       /* Open device */
138       for ( ; !(dev->state & ST_OPENED); ) {
139           if (open_dev(dev, jcr->VolCatInfo.VolCatName, READ_WRITE) < 0) {
140              if (dev->dev_errno == EAGAIN || dev->dev_errno == EBUSY) {
141                 sleep(30);
142              }
143              Jmsg2(jcr, M_FATAL, 0, _("Unable to open device %s. ERR=%s\n"), 
144                 dev_name(dev), strerror_dev(dev));
145              return 0;
146           }
147       }
148
149       /*
150        * Now make sure we have the right tape mounted
151        */
152 read_volume:
153       switch (read_dev_volume_label(jcr, dev, block)) {
154          case VOL_OK:
155             Dmsg1(500, "Vol OK name=%s\n", jcr->VolumeName);
156             memcpy(&dev->VolCatInfo, &jcr->VolCatInfo, sizeof(jcr->VolCatInfo));
157             if (strcmp(dev->VolCatInfo.VolCatStatus, "Recycle") == 0) {
158                recycle = 1;
159             }
160             break;                    /* got it */
161          case VOL_NAME_ERROR:
162             Dmsg1(500, "Vol NAME Error Name=%s\n", jcr->VolumeName);
163             /* Check if we can accept this as an anonymous volume */
164             strcpy(jcr->VolumeName, dev->VolHdr.VolName);
165             if (!dev->capabilities & CAP_ANONVOLS || !dir_get_volume_info(jcr, 1)) {
166                goto mount_next_vol;
167             }
168             Dmsg1(200, "want new name=%s\n", jcr->VolumeName);
169             memcpy(&dev->VolCatInfo, &jcr->VolCatInfo, sizeof(jcr->VolCatInfo));
170             break;
171
172          case VOL_NO_LABEL:
173          case VOL_IO_ERROR:
174             Dmsg1(500, "Vol NO_LABEL or IO_ERROR name=%s\n", jcr->VolumeName);
175             /* If permitted, create a label */
176             if (dev->capabilities & CAP_LABEL) {
177                Dmsg0(190, "Create volume label\n");
178                if (!write_volume_label_to_dev(jcr, (DEVRES *)dev->device, jcr->VolumeName,
179                       jcr->pool_name)) {
180                   goto mount_next_vol;
181                }
182                Jmsg(jcr, M_INFO, 0, _("Created Volume label %s on device %s.\n"),
183                   jcr->VolumeName, dev_name(dev));
184                goto read_volume;      /* read label we just wrote */
185             } 
186             /* NOTE! Fall-through wanted. */
187          default:
188             /* Send error message */
189             Jmsg1(jcr, M_WARNING, 0, "%s", jcr->errmsg);                         
190             if (autochanger) {
191                Jmsg(jcr, M_ERROR, 0, _("Autochanger Volume %s not found in slot %d.\n\
192     Setting slot to zero in catalog.\n"),
193                   jcr->VolumeName, jcr->VolCatInfo.Slot);
194                jcr->VolCatInfo.Slot = 0; /* invalidate slot */
195                dir_update_volume_info(jcr, &jcr->VolCatInfo, 1);  /* set slot */
196             }
197             goto mount_next_vol;
198       }
199       break;
200    }
201
202    /* 
203     * See if we have a fresh tape or tape with data.
204     *
205     * Note, if the LabelType is PRE_LABEL, it was labeled
206     *  but never written. If so, rewrite the label but set as
207     *  VOL_LABEL.  We rewind and return the label (reconstructed)
208     *  in the block so that in the case of a new tape, data can
209     *  be appended just after the block label.  If we are writing
210     *  an second volume, the calling routine will write the label
211     *  before writing the overflow block.
212     *
213     *  If the tape is marked as Recycle, we rewrite the label.
214     */
215    if (dev->VolHdr.LabelType == PRE_LABEL || recycle) {
216       Dmsg1(190, "ready_for_append found freshly labeled volume. dev=%x\n", dev);
217       dev->VolHdr.LabelType = VOL_LABEL; /* set Volume label */
218       write_volume_label_to_block(jcr, dev, block);
219       /*
220        * Write the block now to ensure we have write permission.
221        *  It is better to find out now rather than later.
222        */
223       dev->VolCatInfo.VolCatBytes = 0;
224       if (!rewind_dev(dev)) {
225          Jmsg2(jcr, M_WARNING, 0, _("Rewind error on device %s. ERR=%s\n"), 
226                dev_name(dev), strerror_dev(dev));
227       }
228       if (recycle) {
229          if (!truncate_dev(dev)) {
230             Jmsg2(jcr, M_WARNING, 0, _("Truncate error on device %s. ERR=%s\n"), 
231                   dev_name(dev), strerror_dev(dev));
232          }
233       }
234       if (!write_block_to_dev(dev, block)) {
235          Jmsg2(jcr, M_ERROR, 0, _("Unable to write device %s. ERR=%s\n"),
236             dev_name(dev), strerror_dev(dev));
237          goto mount_next_vol;
238       }
239       if (!rewind_dev(dev)) {
240          Jmsg2(jcr, M_ERROR, 0, _("Unable to rewind device %s. ERR=%s\n"),
241             dev_name(dev), strerror_dev(dev));
242          goto mount_next_vol;
243       }
244       /* Recreate a correct volume label and return it in the block */
245       write_volume_label_to_block(jcr, dev, block);
246       dev->VolCatInfo.VolCatJobs = 1;
247       dev->VolCatInfo.VolCatFiles = 1;
248       dev->VolCatInfo.VolCatErrors = 0;
249       dev->VolCatInfo.VolCatBlocks = 1;
250       if (recycle) {
251          dev->VolCatInfo.VolCatMounts++;  
252          dev->VolCatInfo.VolCatRecycles++;
253       } else {
254          dev->VolCatInfo.VolCatMounts = 1;
255          dev->VolCatInfo.VolCatRecycles = 0;
256          dev->VolCatInfo.VolCatWrites = 1;
257          dev->VolCatInfo.VolCatReads = 1;
258       }
259       strcpy(dev->VolCatInfo.VolCatStatus, "Append");
260       Dmsg0(100, "dir_update_vol_info. Set Append\n");
261       dir_update_volume_info(jcr, &dev->VolCatInfo, 1);  /* indicate doing relabel */
262       if (recycle) {
263          Jmsg(jcr, M_INFO, 0, _("Recycled volume %s on device %s, all previous data lost.\n"),
264             jcr->VolumeName, dev_name(dev));
265       } else {
266          Jmsg(jcr, M_INFO, 0, _("Wrote label to prelabeled Volume %s on device %s\n"),
267             jcr->VolumeName, dev_name(dev));
268       }
269
270    } else {
271       /*
272        * OK, at this point, we have a valid Bacula label, but
273        * we need to position to the end of the volume, since we are
274        * just now putting it into append mode.
275        */
276       Dmsg0(200, "Device previously written, moving to end of data\n");
277       Jmsg(jcr, M_INFO, 0, _("Volume %s previously written, moving to end of data.\n"),
278          jcr->VolumeName);
279       if (!eod_dev(dev)) {
280          Jmsg(jcr, M_ERROR, 0, _("Unable to position to end of data %s. ERR=%s\n"),
281             dev_name(dev), strerror_dev(dev));
282          Jmsg(jcr, M_INFO, 0, _("Marking Volume %s in Error in Catalog.\n"),
283             jcr->VolumeName);
284          strcpy(dev->VolCatInfo.VolCatStatus, "Error");
285          Dmsg0(100, "dir_update_vol_info. Set Error.\n");
286          dir_update_volume_info(jcr, &dev->VolCatInfo, 0);
287          goto mount_next_vol;
288       }
289       /* *****FIXME**** we should do some checking for files too */
290       if (dev_is_tape(dev)) {
291          Jmsg(jcr, M_INFO, 0, _("Ready to append to end of Volume at file=%d.\n"), dev_file(dev));
292          /*
293           * Check if we are positioned on the tape at the same place
294           * that the database says we should be.
295           */
296          if (dev->VolCatInfo.VolCatFiles != dev_file(dev) + 1) {
297             /* ****FIXME**** this should refuse to write on tape */
298             Jmsg(jcr, M_ERROR, 0, _("Hey! Num files mismatch! Volume=%d Catalog=%d\n"), 
299                dev_file(dev)+1, dev->VolCatInfo.VolCatFiles);
300          }
301       }
302       /* Update Volume Info -- will be written at end of Job */
303       dev->VolCatInfo.VolCatMounts++;      /* Update mounts */
304       dev->VolCatInfo.VolCatJobs++;
305       /* Return an empty block */
306       empty_block(block);             /* we used it for reading so set for write */
307    }
308    dev->state |= ST_APPEND;
309    Dmsg0(100, "Normal return from read_dev_for_append\n");
310    return 1; 
311 }
312
313
314 int mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
315 {
316    Dmsg2(90, "NumVolumes=%d CurVolume=%d\n", jcr->NumVolumes, jcr->CurVolume);
317    /*
318     * End Of Tape -- mount next Volume (if another specified)
319     */
320    if (jcr->NumVolumes > 1 && jcr->CurVolume < jcr->NumVolumes) {
321       VOL_LIST *vol = jcr->VolList;
322       /* Find next Volume */
323       jcr->CurVolume++;
324       for (int i=1; i<jcr->CurVolume; i++) {
325          vol = vol->next;
326       }
327       pm_strcpy(&jcr->VolumeName, vol->VolumeName);
328       Dmsg1(400, "There is another volume %s.\n", jcr->VolumeName);
329
330       close_dev(dev);
331       dev->state &= ~ST_READ; 
332       if (!acquire_device_for_read(jcr, dev, block)) {
333          Emsg2(M_FATAL, 0, "Cannot open Dev=%s, Vol=%s\n", dev_name(dev),
334                jcr->VolumeName);
335          return 0;
336       }
337       return 1;                       /* next volume mounted */
338    }
339    Dmsg0(90, "End of Device reached.\n");
340    return 0;
341 }
342
343 /*
344  * Called here to do an autoload using the autochanger, if
345  *  configured, and if a Slot has been defined for this Volume.
346  *  On success this routine loads the indicated tape, but the
347  *  label is not read, so it must be verified.
348  *
349  *  Returns: 1 on success
350  *           0 on failure
351  */
352 int autoload_device(JCR *jcr, DEVICE *dev, int writing)
353 {
354    int slot = jcr->VolCatInfo.Slot;
355    int rtn_stat = 0;
356      
357    /*
358     * Handle autoloaders here.  If we cannot autoload it, we
359     *  will return FALSE to ask the sysop.
360     */
361    if (writing && dev->capabilities && CAP_AUTOCHANGER && slot <= 0) {
362       if (dir_find_next_appendable_volume(jcr)) {
363          slot = jcr->VolCatInfo.Slot; 
364       }
365    }
366    Dmsg1(100, "Want changer slot=%d\n", slot);
367
368    if (slot > 0 && jcr->device->changer_name && jcr->device->changer_command) {
369       uint32_t timeout = jcr->device->max_changer_wait;
370       POOLMEM *changer, *results;
371       int status, loaded;
372
373       results = get_pool_memory(PM_MESSAGE);
374       changer = get_pool_memory(PM_FNAME);
375
376       /* Find out what is loaded, zero means device is unloaded */
377       changer = edit_device_codes(jcr, changer, jcr->device->changer_command, 
378                    "loaded");
379       status = run_program(changer, timeout, results);
380       if (status == 0) {
381          loaded = atoi(results);
382       } else {
383          loaded = -1;              /* force unload */
384       }
385       Dmsg1(100, "loaded=%s\n", results);
386
387       /* If bad status or tape we want is not loaded, load it. */
388       if (status != 0 || loaded != slot) { 
389          if (dev->capabilities & CAP_OFFLINEUNMOUNT) {
390             offline_dev(dev);
391          }
392          /* We are going to load a new tape, so close the device */
393          force_close_dev(dev);
394          if (loaded != 0) {        /* must unload drive */
395             Dmsg0(100, "Doing changer unload.\n");
396             Jmsg(jcr, M_INFO, 0, _("Issuing autochanger \"unload\" command.\n"));
397             changer = edit_device_codes(jcr, changer, 
398                         jcr->device->changer_command, "unload");
399             status = run_program(changer, timeout, NULL);
400             Dmsg1(100, "unload status=%d\n", status);
401          }
402          /*
403           * Load the desired cassette    
404           */
405          Dmsg1(100, "Doing changer load slot %d\n", slot);
406          Jmsg(jcr, M_INFO, 0, _("Issuing autochanger \"load slot %d\" command.\n"),
407             slot);
408          changer = edit_device_codes(jcr, changer, 
409                       jcr->device->changer_command, "load");
410          status = run_program(changer, timeout, NULL);
411          Dmsg2(100, "load slot %d status=%d\n", slot, status);
412       }
413       free_pool_memory(changer);
414       free_pool_memory(results);
415       Dmsg1(100, "After changer, status=%d\n", status);
416       if (status == 0) {           /* did we succeed? */
417          rtn_stat = 1;             /* tape loaded by changer */
418       }
419    }
420    return rtn_stat;
421 }
422
423
424
425 /*
426  * Edit codes into ChangerCommand
427  *  %% = %
428  *  %a = archive device name
429  *  %c = changer device name
430  *  %f = Client's name
431  *  %j = Job name
432  *  %o = command
433  *  %s = Slot base 0
434  *  %S = Slot base 1
435  *  %v = Volume name
436  *
437  *
438  *  omsg = edited output message
439  *  imsg = input string containing edit codes (%x)
440  *  cmd = command string (load, unload, ...) 
441  *
442  */
443 static char *edit_device_codes(JCR *jcr, char *omsg, char *imsg, char *cmd) 
444 {
445    char *p;
446    const char *str;
447    char add[20];
448
449    *omsg = 0;
450    Dmsg1(200, "edit_device_codes: %s\n", imsg);
451    for (p=imsg; *p; p++) {
452       if (*p == '%') {
453          switch (*++p) {
454          case '%':
455             str = "%";
456             break;
457          case 'a':
458             str = jcr->device->dev->dev_name;
459             break;
460          case 'c':
461             str = NPRT(jcr->device->changer_name);
462             break;
463          case 'o':
464             str = NPRT(cmd);
465             break;
466          case 's':
467             sprintf(add, "%d", jcr->VolCatInfo.Slot - 1);
468             str = add;
469             break;
470          case 'S':
471             sprintf(add, "%d", jcr->VolCatInfo.Slot);
472             str = add;
473             break;
474          case 'j':                    /* Job name */
475             str = jcr->Job;
476             break;
477          case 'v':
478             str = NPRT(jcr->VolumeName);
479             break;
480          case 'f':
481             str = NPRT(jcr->client_name);
482             break;
483
484          default:
485             add[0] = '%';
486             add[1] = *p;
487             add[2] = 0;
488             str = add;
489             break;
490          }
491       } else {
492          add[0] = *p;
493          add[1] = 0;
494          str = add;
495       }
496       Dmsg1(200, "add_str %s\n", str);
497       pm_strcat(&omsg, (char *)str);
498       Dmsg1(200, "omsg=%s\n", omsg);
499    }
500    return omsg;
501 }