]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/acquire.c
Small tweaks to locking code no substantial change
[bacula/bacula] / bacula / src / stored / acquire.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2009 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *  Routines to acquire and release a device for read/write
30  *
31  *   Kern Sibbald, August MMII
32  *
33  */
34
35 #include "bacula.h"                   /* pull in global headers */
36 #include "stored.h"                   /* pull in Storage Deamon headers */
37
38 /* Forward referenced functions */
39 static void attach_dcr_to_dev(DCR *dcr);
40 static void detach_dcr_from_dev(DCR *dcr);
41 static void set_dcr_from_vol(DCR *dcr, VOL_LIST *vol);
42
43
44 /*********************************************************************
45  * Acquire device for reading. 
46  *  The drive should have previously been reserved by calling 
47  *  reserve_device_for_read(). We read the Volume label from the block and
48  *  leave the block pointers just after the label.
49  *
50  *  Returns: NULL if failed for any reason
51  *           dcr  if successful
52  */
53 bool acquire_device_for_read(DCR *dcr)
54 {
55    DEVICE *dev = dcr->dev;
56    JCR *jcr = dcr->jcr;
57    bool ok = false;
58    bool tape_previously_mounted;
59    bool tape_initially_mounted;
60    VOL_LIST *vol;
61    bool try_autochanger = true;
62    int i;
63    int vol_label_status;
64    int retry = 0;
65    
66    Dmsg2(950, "dcr=%p dev=%p\n", dcr, dcr->dev);
67    Dmsg2(950, "MediaType dcr=%s dev=%s\n", dcr->media_type, dev->device->media_type);
68    dev->dblock(BST_DOING_ACQUIRE);
69
70    if (dev->num_writers > 0) {
71       Jmsg2(jcr, M_FATAL, 0, _("Acquire read: num_writers=%d not zero. Job %d canceled.\n"), 
72          dev->num_writers, jcr->JobId);
73       goto get_out;
74    }
75
76    /* Find next Volume, if any */
77    vol = jcr->VolList;
78    if (!vol) {
79       char ed1[50];
80       Jmsg(jcr, M_FATAL, 0, _("No volumes specified for reading. Job %s canceled.\n"), 
81          edit_int64(jcr->JobId, ed1));
82       goto get_out;
83    }
84    jcr->CurReadVolume++;
85    for (i=1; i<jcr->CurReadVolume; i++) {
86       vol = vol->next;
87    }
88    if (!vol) {
89       Jmsg(jcr, M_FATAL, 0, _("Logic error: no next volume to read. Numvol=%d Curvol=%d\n"),
90          jcr->NumReadVolumes, jcr->CurReadVolume);
91       goto get_out;                   /* should not happen */   
92    }
93    set_dcr_from_vol(dcr, vol);
94
95    Dmsg2(100, "Want Vol=%s Slot=%d\n", vol->VolumeName, vol->Slot);
96     
97    /*
98     * If the MediaType requested for this volume is not the
99     *  same as the current drive, we attempt to find the same
100     *  device that was used to write the orginal volume.  If
101     *  found, we switch to using that device.
102     *
103     *  N.B. A lot of routines rely on the dcr pointer not changing
104     *    read_records.c even has multiple dcrs cached, so we take care
105     *    here to release all important parts of the dcr and re-acquire
106     *    them such as the block pointer (size may change), but we do
107     *    not release the dcr.
108     */
109    Dmsg2(50, "MediaType dcr=%s dev=%s\n", dcr->media_type, dev->device->media_type);
110    if (dcr->media_type[0] && strcmp(dcr->media_type, dev->device->media_type) != 0) {
111       RCTX rctx;
112       DIRSTORE *store;
113       int stat;
114
115       Jmsg3(jcr, M_INFO, 0, _("Changing read device. Want Media Type=\"%s\" have=\"%s\"\n"
116                               "  device=%s\n"), 
117             dcr->media_type, dev->device->media_type, dev->print_name());
118       Dmsg3(50, "Changing read device. Want Media Type=\"%s\" have=\"%s\"\n"
119                               "  device=%s\n", 
120             dcr->media_type, dev->device->media_type, dev->print_name());
121
122       dev->dunblock(DEV_UNLOCKED);
123
124       lock_reservations();
125       memset(&rctx, 0, sizeof(RCTX));
126       rctx.jcr = jcr;
127       jcr->read_dcr = dcr;
128       jcr->reserve_msgs = New(alist(10, not_owned_by_alist));
129       rctx.any_drive = true;
130       rctx.device_name = vol->device;
131       store = new DIRSTORE;
132       memset(store, 0, sizeof(DIRSTORE));
133       store->name[0] = 0; /* No dir name */
134       bstrncpy(store->media_type, vol->MediaType, sizeof(store->media_type));
135       bstrncpy(store->pool_name, dcr->pool_name, sizeof(store->pool_name));
136       bstrncpy(store->pool_type, dcr->pool_type, sizeof(store->pool_type));
137       store->append = false;
138       rctx.store = store;
139       clean_device(dcr);                     /* clean up the dcr */
140       
141       /*
142        * Search for a new device
143        */
144       stat = search_res_for_device(rctx);
145       release_reserve_messages(jcr);         /* release queued messages */
146       unlock_reservations();
147
148       if (stat == 1) {
149          dev = dcr->dev;                     /* get new device pointer */
150          dev->dblock(BST_DOING_ACQUIRE); 
151          dcr->VolumeName[0] = 0;
152          Jmsg(jcr, M_INFO, 0, _("Media Type change.  New read device %s chosen.\n"),
153             dev->print_name());
154          Dmsg1(50, "Media Type change.  New read device %s chosen.\n", dev->print_name());
155
156          bstrncpy(dcr->VolumeName, vol->VolumeName, sizeof(dcr->VolumeName));
157          bstrncpy(dcr->VolCatInfo.VolCatName, vol->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
158          bstrncpy(dcr->media_type, vol->MediaType, sizeof(dcr->media_type));
159          dcr->VolCatInfo.Slot = vol->Slot;
160          dcr->VolCatInfo.InChanger = vol->Slot > 0; 
161          bstrncpy(dcr->pool_name, store->pool_name, sizeof(dcr->pool_name));
162          bstrncpy(dcr->pool_type, store->pool_type, sizeof(dcr->pool_type));
163       } else {
164          /* error */
165          Jmsg1(jcr, M_FATAL, 0, _("No suitable device found to read Volume \"%s\"\n"),
166             vol->VolumeName);
167          Dmsg1(50, "No suitable device found to read Volume \"%s\"\n", vol->VolumeName);
168          goto get_out;
169       }
170    }
171    Dmsg2(400, "MediaType dcr=%s dev=%s\n", dcr->media_type, dev->device->media_type);
172
173    dev->clear_unload();
174
175    if (dev->vol && dev->vol->is_swapping()) {
176       dev->vol->set_slot(vol->Slot);
177       Dmsg3(100, "swapping: slot=%d Vol=%s dev=%s\n", dev->vol->get_slot(),
178          dev->vol->vol_name, dev->print_name());
179    }
180
181    init_device_wait_timers(dcr);
182
183    tape_previously_mounted = dev->can_read() || dev->can_append() ||
184                              dev->is_labeled();
185    tape_initially_mounted = tape_previously_mounted;
186
187
188    /* Volume info is always needed because of VolParts */
189    Dmsg1(150, "dir_get_volume_info vol=%s\n", dcr->VolumeName);
190    if (!dir_get_volume_info(dcr, GET_VOL_INFO_FOR_READ)) {
191       Dmsg2(150, "dir_get_vol_info failed for vol=%s: %s\n", 
192          dcr->VolumeName, jcr->errmsg);
193       Jmsg1(jcr, M_WARNING, 0, "Read acquire: %s", jcr->errmsg);
194    }
195    dev->set_load();                /* set to load volume */
196    
197    for ( ;; ) {
198       /* If not polling limit retries */
199       if (!dev->poll && retry++ > 10) {
200          break;
201       }
202       dev->clear_labeled();              /* force reread of label */
203       if (job_canceled(jcr)) {
204          char ed1[50];
205          Mmsg1(dev->errmsg, _("Job %s canceled.\n"), edit_int64(jcr->JobId, ed1));
206          Jmsg(jcr, M_INFO, 0, dev->errmsg);
207          goto get_out;                /* error return */
208       }
209
210       dcr->do_unload();
211       dcr->do_swapping(false/*!is_writing*/);
212       dcr->do_load(false /*!is_writing*/);
213       set_dcr_from_vol(dcr, vol);          /* refresh dcr with desired volume info */
214
215       /*
216        * This code ensures that the device is ready for
217        * reading. If it is a file, it opens it.
218        * If it is a tape, it checks the volume name
219        */
220       Dmsg1(100, "bstored: open vol=%s\n", dcr->VolumeName);
221       if (dev->open(dcr, OPEN_READ_ONLY) < 0) {
222          if (!dev->poll) {
223             Jmsg3(jcr, M_WARNING, 0, _("Read open device %s Volume \"%s\" failed: ERR=%s\n"),
224                   dev->print_name(), dcr->VolumeName, dev->bstrerror());
225          }
226          goto default_path;
227       }
228       Dmsg1(50, "opened dev %s OK\n", dev->print_name());
229       
230       /* Read Volume Label */
231       Dmsg0(50, "calling read-vol-label\n");
232       vol_label_status = read_dev_volume_label(dcr);
233       switch (vol_label_status) {
234       case VOL_OK:
235          Dmsg0(50, "Got correct volume.\n");
236          ok = true;
237          dev->VolCatInfo = dcr->VolCatInfo;     /* structure assignment */
238          break;                    /* got it */
239       case VOL_IO_ERROR:
240          Dmsg0(50, "IO Error\n");
241          /*
242           * Send error message generated by read_dev_volume_label()
243           *  only we really had a tape mounted. This supresses superfluous
244           *  error messages when nothing is mounted.
245           */
246          if (tape_previously_mounted) {
247             Jmsg(jcr, M_WARNING, 0, "Read acquire: %s", jcr->errmsg);
248          }
249          goto default_path;
250       case VOL_NAME_ERROR:
251          Dmsg3(50, "Vol name=%s want=%s drv=%s.\n", dev->VolHdr.VolumeName, 
252                dcr->VolumeName, dev->print_name());
253          if (dev->is_volume_to_unload()) {
254             goto default_path;
255          }
256          dev->set_unload();              /* force unload of unwanted tape */
257          if (!unload_autochanger(dcr, -1)) {
258             /* at least free the device so we can re-open with correct volume */
259             dev->close();                                                          
260             free_volume(dev);
261          }
262          dev->set_load();
263          /* Fall through */
264       default:
265          Jmsg1(jcr, M_WARNING, 0, "Read acquire: %s", jcr->errmsg);
266 default_path:
267          Dmsg0(50, "default path\n");
268          tape_previously_mounted = true;
269          
270          /*
271           * If the device requires mount, close it, so the device can be ejected.
272           */
273          if (dev->requires_mount()) {
274             dev->close();
275             free_volume(dev);
276          }
277          
278          /* Call autochanger only once unless ask_sysop called */
279          if (try_autochanger) {
280             int stat;
281             Dmsg2(200, "calling autoload Vol=%s Slot=%d\n",
282                dcr->VolumeName, dcr->VolCatInfo.Slot);
283             stat = autoload_device(dcr, 0, NULL);
284             if (stat > 0) {
285                try_autochanger = false;
286                continue;              /* try reading volume mounted */
287             }
288          }
289          
290          /* Mount a specific volume and no other */
291          Dmsg0(200, "calling dir_ask_sysop\n");
292          if (!dir_ask_sysop_to_mount_volume(dcr, ST_READ)) {
293             goto get_out;             /* error return */
294          }
295
296          /* Volume info is always needed because of VolParts */
297          Dmsg1(150, "dir_get_volume_info vol=%s\n", dcr->VolumeName);
298          if (!dir_get_volume_info(dcr, GET_VOL_INFO_FOR_READ)) {
299             Dmsg2(150, "dir_get_vol_info failed for vol=%s: %s\n", 
300                   dcr->VolumeName, jcr->errmsg);
301             Jmsg1(jcr, M_WARNING, 0, "Read acquire: %s", jcr->errmsg);
302          }
303          dev->set_load();                /* set to load volume */
304
305          try_autochanger = true;      /* permit trying the autochanger again */
306
307          continue;                    /* try reading again */
308       } /* end switch */
309       break;
310    } /* end for loop */
311
312    if (!ok) {
313       Jmsg1(jcr, M_FATAL, 0, _("Too many errors trying to mount device %s for reading.\n"),
314             dev->print_name());
315       goto get_out;
316    }
317
318    dev->clear_append();
319    dev->set_read();
320    set_jcr_job_status(jcr, JS_Running);
321    dir_send_job_status(jcr);
322    Jmsg(jcr, M_INFO, 0, _("Ready to read from volume \"%s\" on device %s.\n"),
323       dcr->VolumeName, dev->print_name());
324
325 get_out:
326    dev->dlock();
327    dcr->clear_reserved();
328    /* 
329     * Normally we are blocked, but in at least one error case above 
330     *   we are not blocked because we unsuccessfully tried changing
331     *   devices.  
332     */
333    if (dev->is_blocked()) {
334       dev->dunblock(DEV_LOCKED);
335    } else {
336       dev->dunlock();               /* dunblock() unlock the device too */
337    }
338    Dmsg2(950, "dcr=%p dev=%p\n", dcr, dcr->dev);
339    Dmsg2(950, "MediaType dcr=%s dev=%s\n", dcr->media_type, dev->device->media_type);
340    return ok;
341 }
342
343 /*
344  * Acquire device for writing. We permit multiple writers.
345  *  If this is the first one, we read the label.
346  *
347  *  Returns: NULL if failed for any reason
348  *           dcr if successful.
349  *   Note, normally reserve_device_for_append() is called
350  *   before this routine.
351  */
352 DCR *acquire_device_for_append(DCR *dcr)
353 {
354    DEVICE *dev = dcr->dev;
355    JCR *jcr = dcr->jcr;
356    bool ok = false;
357    bool have_vol = false;
358
359    init_device_wait_timers(dcr);
360
361    P(dev->acquire_mutex);           /* only one job at a time */
362    dev->dlock();
363    Dmsg1(100, "acquire_append device is %s\n", dev->is_tape()?"tape":
364         (dev->is_dvd()?"DVD":"disk"));
365
366    /*
367     * With the reservation system, this should not happen
368     */
369    if (dev->can_read()) {
370       Jmsg1(jcr, M_FATAL, 0, _("Want to append, but device %s is busy reading.\n"), dev->print_name());
371       Dmsg1(200, "Want to append but device %s is busy reading.\n", dev->print_name());
372       goto get_out;
373    }
374
375    dev->clear_unload();
376
377    /*
378     * have_vol defines whether or not mount_next_write_volume should
379     *   ask the Director again about what Volume to use.
380     */
381    if (dev->can_append() && dcr->is_suitable_volume_mounted() &&
382        strcmp(dcr->VolCatInfo.VolCatStatus, "Recycle") != 0) {
383       Dmsg0(190, "device already in append.\n");
384       /*
385        * At this point, the correct tape is already mounted, so
386        *   we do not need to do mount_next_write_volume(), unless
387        *   we need to recycle the tape.
388        */
389        if (dev->num_writers == 0) {
390           memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
391        }
392        have_vol = dcr->is_tape_position_ok();
393    }
394
395    if (!have_vol) {
396       dev->r_dlock(true);
397       block_device(dev, BST_DOING_ACQUIRE);
398       dev->dunlock();
399       Dmsg1(190, "jid=%u Do mount_next_write_vol\n", (uint32_t)jcr->JobId);
400       if (!dcr->mount_next_write_volume()) {
401          if (!job_canceled(jcr)) {
402             /* Reduce "noise" -- don't print if job canceled */
403             Jmsg(jcr, M_FATAL, 0, _("Could not ready device %s for append.\n"),
404                dev->print_name());
405             Dmsg1(200, "Could not ready device %s for append.\n", 
406                dev->print_name());
407          }
408          dev->dlock();
409          unblock_device(dev);
410          goto get_out;
411       }
412       Dmsg2(190, "Output pos=%u:%u\n", dcr->dev->file, dcr->dev->block_num);
413       dev->dlock();
414       unblock_device(dev);
415    }
416
417    dev->num_writers++;                /* we are now a writer */
418    if (jcr->NumWriteVolumes == 0) {
419       jcr->NumWriteVolumes = 1;
420    }
421    dev->VolCatInfo.VolCatJobs++;              /* increment number of jobs on vol */
422    Dmsg4(100, "=== nwriters=%d nres=%d vcatjob=%d dev=%s\n", 
423       dev->num_writers, dev->num_reserved(), dev->VolCatInfo.VolCatJobs, 
424       dev->print_name());
425    dir_update_volume_info(dcr, false, false); /* send Volume info to Director */
426    ok = true;
427
428 get_out:
429    dcr->clear_reserved();
430    dev->dunlock();
431    V(dev->acquire_mutex);
432    return ok ? dcr : NULL;
433 }
434
435 /*
436  * This job is done, so release the device. From a Unix standpoint,
437  *  the device remains open.
438  *
439  * Note, if we are spooling, we may enter with the device locked.
440  * However, in all cases, unlock the device when leaving.
441  *
442  */
443 bool release_device(DCR *dcr)
444 {
445    JCR *jcr = dcr->jcr;
446    DEVICE *dev = dcr->dev;
447    bool ok = true;
448    char tbuf[100];
449
450    /* lock only if not already locked by this thread */
451    if (!dcr->is_dev_locked()) {
452       dev->r_dlock();
453    }
454    lock_volumes();
455    Dmsg2(100, "release_device device %s is %s\n", dev->print_name(), dev->is_tape()?"tape":"disk");
456
457    /* if device is reserved, job never started, so release the reserve here */
458    dcr->clear_reserved();
459
460    if (dev->can_read()) {
461       VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
462       dev->clear_read();              /* clear read bit */
463       Dmsg2(150, "dir_update_vol_info. label=%d Vol=%s\n",
464          dev->is_labeled(), vol->VolCatName);
465       if (dev->is_labeled() && vol->VolCatName[0] != 0) {
466          dir_update_volume_info(dcr, false, false); /* send Volume info to Director */
467          remove_read_volume(jcr, dcr->VolumeName);
468          volume_unused(dcr);
469       }
470    } else if (dev->num_writers > 0) {
471       /* 
472        * Note if WEOT is set, we are at the end of the tape
473        *   and may not be positioned correctly, so the
474        *   job_media_record and update_vol_info have already been
475        *   done, which means we skip them here.
476        */
477       dev->num_writers--;
478       Dmsg1(100, "There are %d writers in release_device\n", dev->num_writers);
479       if (dev->is_labeled()) {
480          Dmsg2(200, "dir_create_jobmedia. Release vol=%s dev=%s\n", 
481                dev->VolCatInfo.VolCatName, dev->print_name());
482          if (!dev->at_weot() && !dir_create_jobmedia_record(dcr)) {
483             Jmsg2(jcr, M_FATAL, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"),
484                dcr->VolCatInfo.VolCatName, jcr->Job);
485          }
486          /* If no more writers, and no errors, and wrote something, write an EOF */
487          if (!dev->num_writers && dev->can_write() && dev->block_num > 0) {
488             dev->weof(1);
489             write_ansi_ibm_labels(dcr, ANSI_EOF_LABEL, dev->VolHdr.VolumeName);
490          }
491          if (!dev->at_weot()) {
492             dev->VolCatInfo.VolCatFiles = dev->file;   /* set number of files */
493             /* Note! do volume update before close, which zaps VolCatInfo */
494             dir_update_volume_info(dcr, false, false); /* send Volume info to Director */
495             Dmsg2(200, "dir_update_vol_info. Release vol=%s dev=%s\n", 
496                   dev->VolCatInfo.VolCatName, dev->print_name());
497          }
498          if (dev->num_writers == 0) {         /* if not being used */
499             volume_unused(dcr);               /*  we obviously are not using the volume */
500          }
501       }
502
503    } else {
504       /*                
505        * If we reach here, it is most likely because the job
506        *   has failed, since the device is not in read mode and
507        *   there are no writers. It was probably reserved.
508        */
509       volume_unused(dcr);
510    }
511    unlock_volumes();
512    Dmsg3(100, "%d writers, %d reserve, dev=%s\n", dev->num_writers, dev->num_reserved(),
513          dev->print_name());
514
515
516    /* If no writers, close if file or !CAP_ALWAYS_OPEN */
517    if (dev->num_writers == 0 && (!dev->is_tape() || !dev->has_cap(CAP_ALWAYSOPEN))) {
518       dvd_remove_empty_part(dcr);        /* get rid of any empty spool part */
519       dev->close();
520       free_volume(dev);
521    }
522
523    /* Fire off Alert command and include any output */
524    if (!job_canceled(jcr) && dcr->device->alert_command) {
525       POOLMEM *alert;
526       int status = 1;
527       BPIPE *bpipe;
528       char line[MAXSTRING];
529       alert = get_pool_memory(PM_FNAME);
530       alert = edit_device_codes(dcr, alert, dcr->device->alert_command, "");
531       bpipe = open_bpipe(alert, 0, "r");
532       if (bpipe) {
533          while (fgets(line, sizeof(line), bpipe->rfd)) {
534             Jmsg(jcr, M_ALERT, 0, _("Alert: %s"), line);
535          }
536          status = close_bpipe(bpipe);
537       } else {
538          status = errno;
539       }
540       if (status != 0) {
541          berrno be;
542          Jmsg(jcr, M_ALERT, 0, _("3997 Bad alert command: %s: ERR=%s.\n"),
543               alert, be.bstrerror(status));
544       }
545
546       Dmsg1(400, "alert status=%d\n", status);
547       free_pool_memory(alert);
548    }
549    pthread_cond_broadcast(&dev->wait_next_vol);
550    Dmsg2(100, "JobId=%u broadcast wait_device_release at %s\n", 
551          (uint32_t)jcr->JobId, bstrftimes(tbuf, sizeof(tbuf), (utime_t)time(NULL)));
552    pthread_cond_broadcast(&wait_device_release);
553    dev->dunlock();
554    if (dcr->keep_dcr) {
555       detach_dcr_from_dev(dcr);
556    } else {
557       free_dcr(dcr);
558    }
559    Dmsg2(100, "===== Device %s released by JobId=%u\n", dev->print_name(),
560          (uint32_t)jcr->JobId);
561    return ok;
562 }
563
564 /*
565  * Clean up the device for reuse without freeing the memory
566  */
567 bool clean_device(DCR *dcr)
568 {
569    bool ok;
570    dcr->keep_dcr = true;                  /* do not free the dcr */
571    ok = release_device(dcr);
572    dcr->keep_dcr = false;
573    return ok;
574 }
575
576 /*
577  * Create a new Device Control Record and attach
578  *   it to the device (if this is a real job).
579  * Note, this has been updated so that it can be called first 
580  *   without a DEVICE, then a second or third time with a DEVICE,
581  *   and each time, it should cleanup and point to the new device.
582  *   This should facilitate switching devices.
583  * Note, each dcr must point to the controlling job (jcr).  However,
584  *   a job can have multiple dcrs, so we must not store in the jcr's
585  *   structure as previously. The higher level routine must store
586  *   this dcr in the right place
587  *
588  */
589 DCR *new_dcr(JCR *jcr, DCR *dcr, DEVICE *dev)
590 {
591    if (!dcr) {
592       int errstat;
593       dcr = (DCR *)malloc(sizeof(DCR));
594       memset(dcr, 0, sizeof(DCR));
595       dcr->tid = pthread_self();
596       dcr->spool_fd = -1;
597       if ((errstat = pthread_mutex_init(&dcr->m_mutex, NULL)) != 0) {
598          berrno be;
599          dev->dev_errno = errstat;
600          Mmsg1(dev->errmsg, _("Unable to init mutex: ERR=%s\n"), be.bstrerror(errstat));
601          Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
602       }
603    }
604    dcr->jcr = jcr;                 /* point back to jcr */
605    /* Set device information, possibly change device */
606    if (dev) {
607       if (dcr->block) {
608          free_block(dcr->block);
609       }
610       dcr->block = new_block(dev);
611       if (dcr->rec) {
612          free_record(dcr->rec);
613       }
614       dcr->rec = new_record();
615       if (dcr->attached_to_dev) {
616          detach_dcr_from_dev(dcr);
617       }
618       /* Use job spoolsize prior to device spoolsize */
619       if (jcr->spool_size) {
620          dcr->max_job_spool_size = jcr->spool_size;
621       } else {
622          dcr->max_job_spool_size = dev->device->max_job_spool_size;
623       }
624       dcr->device = dev->device;
625       dcr->dev = dev;
626       attach_dcr_to_dev(dcr);
627    }
628    return dcr;
629 }
630
631 /*
632  * Search the dcrs list for the given dcr. If it is found,
633  *  as it should be, then remove it. Also zap the jcr pointer
634  *  to the dcr if it is the same one.
635  *
636  * Note, this code will be turned on when we can write to multiple
637  *  dcrs at the same time.
638  */
639 #ifdef needed
640 static void remove_dcr_from_dcrs(DCR *dcr)
641 {
642    JCR *jcr = dcr->jcr;
643    if (jcr->dcrs) {
644       int i = 0;
645       DCR *ldcr;
646       int num = jcr->dcrs->size();
647       for (i=0; i < num; i++) {
648          ldcr = (DCR *)jcr->dcrs->get(i);
649          if (ldcr == dcr) {
650             jcr->dcrs->remove(i);
651             if (jcr->dcr == dcr) {
652                jcr->dcr = NULL;
653             }
654          }
655       }
656    }
657 }
658 #endif
659
660 static void attach_dcr_to_dev(DCR *dcr)
661 {
662    DEVICE *dev;
663    JCR *jcr;
664
665    P(dcr->m_mutex);
666    dev = dcr->dev;
667    jcr = dcr->jcr;
668    if (jcr) Dmsg1(500, "JobId=%u enter attach_dcr_to_dev\n", (uint32_t)jcr->JobId);
669    if (!dcr->attached_to_dev && dev->initiated && jcr && jcr->getJobType() != JT_SYSTEM) {
670       dev->attached_dcrs->append(dcr);  /* attach dcr to device */
671       dcr->attached_to_dev = true;
672       Dmsg1(500, "JobId=%u attach_dcr_to_dev\n", (uint32_t)jcr->JobId);
673    }
674    V(dcr->m_mutex);
675 }
676
677 /* 
678  * DCR is locked before calling this routine
679  */
680 static void locked_detach_dcr_from_dev(DCR *dcr)
681 {
682    DEVICE *dev = dcr->dev;
683    Dmsg0(500, "Enter detach_dcr_from_dev\n"); /* jcr is NULL in some cases */
684
685    /* Detach this dcr only if attached */
686    if (dcr->attached_to_dev && dev) {
687       dev->dlock();
688       dcr->unreserve_device();
689       dcr->dev->attached_dcrs->remove(dcr);  /* detach dcr from device */
690 //    remove_dcr_from_dcrs(dcr);      /* remove dcr from jcr list */
691       dev->dunlock();
692    }
693    dcr->attached_to_dev = false;
694 }
695
696
697 static void detach_dcr_from_dev(DCR *dcr)
698 {
699    P(dcr->m_mutex);
700    locked_detach_dcr_from_dev(dcr);
701    V(dcr->m_mutex);
702 }
703
704 /*
705  * Free up all aspects of the given dcr -- i.e. dechain it,
706  *  release allocated memory, zap pointers, ...
707  */
708 void free_dcr(DCR *dcr)
709 {
710    JCR *jcr;
711
712    P(dcr->m_mutex);
713    jcr = dcr->jcr;
714    locked_detach_dcr_from_dev(dcr);
715
716    if (dcr->block) {
717       free_block(dcr->block);
718    }
719    if (dcr->rec) {
720       free_record(dcr->rec);
721    }
722    if (jcr && jcr->dcr == dcr) {
723       jcr->dcr = NULL;
724    }
725    if (jcr && jcr->read_dcr == dcr) {
726       jcr->read_dcr = NULL;
727    }
728    V(dcr->m_mutex);
729    pthread_mutex_destroy(&dcr->m_mutex);
730    free(dcr);
731 }
732
733 static void set_dcr_from_vol(DCR *dcr, VOL_LIST *vol)
734 {
735    /*    
736     * Note, if we want to be able to work from a .bsr file only          
737     *  for disaster recovery, we must "simulate" reading the catalog
738     */
739    bstrncpy(dcr->VolumeName, vol->VolumeName, sizeof(dcr->VolumeName));
740    bstrncpy(dcr->VolCatInfo.VolCatName, vol->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
741    bstrncpy(dcr->media_type, vol->MediaType, sizeof(dcr->media_type));
742    dcr->VolCatInfo.Slot = vol->Slot;
743    dcr->VolCatInfo.InChanger = vol->Slot > 0; 
744 }