]> git.sur5r.net Git - bacula/bacula/commitdiff
kes Split volume management code out of src/stored/reserve.c into
authorKern Sibbald <kern@sibbald.com>
Tue, 21 Oct 2008 14:24:10 +0000 (14:24 +0000)
committerKern Sibbald <kern@sibbald.com>
Tue, 21 Oct 2008 14:24:10 +0000 (14:24 +0000)
     a new file vol_mgr.c
kes  Modify configure to do an automatic make clean.  This ensures
     that any changes to ./configure options are handled correctly.

git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@7866 91ce42f0-d328-0410-95d8-f526ca767f89

bacula/autoconf/configure.in
bacula/configure
bacula/src/stored/Makefile.in
bacula/src/stored/protos.h
bacula/src/stored/reserve.c
bacula/src/stored/vol_mgr.c [new file with mode: 0644]
bacula/technotes-2.5

index 29bc8c5b28a99d4823ada9901d5c61d630dc9e59..9510443bd300db58348bac75dfb0b5bd65780c6e 100644 (file)
@@ -2813,6 +2813,12 @@ if test X"$GCC" = "Xyes" ; then
    fi
 fi
 
+# clean up any old junk
+echo " "
+echo "Cleaning up"
+echo " "
+make clean
+
 if test "x${db_type}" = "xInternal" ; then
    echo " "
    echo " "
index d4721c23275205e73714b355afbe90aa13665e33..b8daeb6dc5efa0012da4dc19e7d65ab433a9f429 100755 (executable)
@@ -44139,6 +44139,12 @@ if test X"$GCC" = "Xyes" ; then
    fi
 fi
 
+# clean up any old junk
+echo " "
+echo "Cleaning up"
+echo " "
+make clean
+
 if test "x${db_type}" = "xInternal" ; then
    echo " "
    echo " "
index aa56eb3d4a9258144b7f9e5f3b738d23043248be..d9a74e41d169685d99fc2a9673a183800f0af210 100644 (file)
@@ -28,39 +28,42 @@ SDOBJS =  stored.o ansi_label.o vtape.o \
          label.o lock.o mac.o match_bsr.o mount.o parse_bsr.o \
          pythonsd.o read.o read_record.o record.o \
          reserve.o scan.o sd_plugins.o \
-         spool.o status.o stored_conf.o wait.o
+         spool.o status.o stored_conf.o vol_mgr.o wait.o
 
 # btape
 TAPEOBJS = btape.o block.o butil.o dev.o device.o label.o vtape.o \
           lock.o ansi_label.o dvd.o ebcdic.o \
           autochanger.o acquire.o mount.o record.o read_record.o \
-          reserve.o \
-          stored_conf.o match_bsr.o parse_bsr.o scan.o spool.o wait.o
+          reserve.o stored_conf.o match_bsr.o parse_bsr.o scan.o \
+          spool.o vol_mgr.o wait.o
 
 # bls
 BLSOBJS = bls.o block.o butil.o device.o dev.o label.o match_bsr.o vtape.o \
          ansi_label.o dvd.o ebcdic.o lock.o \
          autochanger.o acquire.o mount.o parse_bsr.o record.o  \
-         read_record.o reserve.o scan.o stored_conf.o spool.o wait.o
+         read_record.o reserve.o scan.o stored_conf.o spool.o \
+         vol_mgr.o wait.o
 
 # bextract
 BEXTOBJS = bextract.o block.o device.o dev.o label.o record.o vtape.o \
           ansi_label.o dvd.o ebcdic.o lock.o \
           autochanger.o acquire.o mount.o match_bsr.o parse_bsr.o butil.o \
-          read_record.o reserve.o scan.o stored_conf.o spool.o wait.o
+          read_record.o reserve.o scan.o stored_conf.o spool.o \
+          vol_mgr.o wait.o
 
 # bscan
 SCNOBJS = bscan.o block.o device.o dev.o label.o vtape.o \
          ansi_label.o dvd.o ebcdic.o lock.o \
          autochanger.o acquire.o mount.o record.o match_bsr.o parse_bsr.o \
-         butil.o read_record.o scan.o reserve.o stored_conf.o spool.o wait.o
+         butil.o read_record.o scan.o reserve.o stored_conf.o spool.o \
+         vol_mgr.o wait.o
 
 # bcopy
 COPYOBJS = bcopy.o block.o device.o dev.o label.o vtape.o \
           ansi_label.o dvd.o ebcdic.o lock.o \
           autochanger.o acquire.o mount.o record.o match_bsr.o parse_bsr.o \
           butil.o read_record.o reserve.o \
-          scan.o stored_conf.o spool.o wait.o
+          scan.o stored_conf.o spool.o vol_mgr.o wait.o
 
 
 
@@ -87,61 +90,61 @@ all: Makefile bacula-sd @STATIC_SD@ bls bextract bscan btape bcopy
 bacula-sd: Makefile $(SDOBJS) ../lib/libbacpy$(DEFAULT_ARCHIVE_TYPE) ../lib/libbaccfg$(DEFAULT_ARCHIVE_TYPE) ../lib/libbac$(DEFAULT_ARCHIVE_TYPE)
        @echo "Linking $@ ..."
        $(LIBTOOL_LINK) $(CXX) $(WLDFLAGS) $(LDFLAGS) -L../lib -o $@ $(SDOBJS) $(FDLIBS) \
-       -lbacpy -lbaccfg -lbac -lm $(PYTHON_LIBS) $(DLIB) $(LIBS) $(WRAPLIBS) \
+          -lbacpy -lbaccfg -lbac -lm $(PYTHON_LIBS) $(DLIB) $(LIBS) $(WRAPLIBS) \
        $(GETTEXT_LIBS) $(OPENSSL_LIBS)
 
 static-bacula-sd: Makefile $(SDOBJS) ../lib/libbacpy$(DEFAULT_ARCHIVE_TYPE) ../lib/libbaccfg$(DEFAULT_ARCHIVE_TYPE) ../lib/libbac$(DEFAULT_ARCHIVE_TYPE)
        $(LIBTOOL_LINK) $(CXX) $(WLDFLAGS) $(LDFLAGS) -static -L../lib -o $@ $(SDOBJS) $(FDLIBS) \
-       -lbacpy -lbaccfg -lbac -lm $(PYTHON_LIBS) $(DLIB) $(LIBS) $(WRAPLIBS) \
+          -lbacpy -lbaccfg -lbac -lm $(PYTHON_LIBS) $(DLIB) $(LIBS) $(WRAPLIBS) \
        $(GETTEXT_LIBS) $(OPENSSL_LIBS)
        strip $@
 
 btape.o: btape.c
        @echo "Compiling $<"
        $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(CPPFLAGS) -I$(srcdir) \
-       -I$(basedir) $(DINCLUDE) $(CFLAGS) $<
+          -I$(basedir) $(DINCLUDE) $(CFLAGS) $<
 
 btape: Makefile  $(TAPEOBJS) ../lib/libbac$(DEFAULT_ARCHIVE_TYPE) ../lib/libbaccfg$(DEFAULT_ARCHIVE_TYPE) ../cats/libbacsql$(DEFAULT_ARCHIVE_TYPE)
        $(LIBTOOL_LINK) $(CXX) $(TTOOL_LDFLAGS) $(LDFLAGS) -L../lib -L../cats  -o $@ $(TAPEOBJS) \
-       -lbacsql -lbaccfg -lbac $(DLIB) -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS)
+          -lbacsql -lbaccfg -lbac $(DLIB) -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS)
 
 bls.o: bls.c
        @echo "Compiling $<"
        $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(CPPFLAGS) -I$(srcdir) \
-       -I$(basedir) $(DINCLUDE) $(CFLAGS) $<
+          -I$(basedir) $(DINCLUDE) $(CFLAGS) $<
 
 bls:   Makefile $(BLSOBJS) ../findlib/libbacfind$(DEFAULT_ARCHIVE_TYPE) ../lib/libbaccfg$(DEFAULT_ARCHIVE_TYPE) ../lib/libbac$(DEFAULT_ARCHIVE_TYPE)
        @echo "Compiling $<"
        $(LIBTOOL_LINK) $(CXX) $(TTOOL_LDFLAGS) $(LDFLAGS) -L../lib -L../findlib -o $@ $(BLSOBJS) $(DLIB) \
-       -lbacfind -lbaccfg -lbac -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS)
+          -lbacfind -lbaccfg -lbac -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS)
 
 bextract.o: bextract.c
        @echo "Compiling $<"
        $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(CPPFLAGS) -I$(srcdir) \
-       -I$(basedir) $(DINCLUDE) $(CFLAGS) $<
+          -I$(basedir) $(DINCLUDE) $(CFLAGS) $<
 
 bextract: Makefile $(BEXTOBJS) ../findlib/libbacfind$(DEFAULT_ARCHIVE_TYPE) ../lib/libbaccfg$(DEFAULT_ARCHIVE_TYPE) ../lib/libbac$(DEFAULT_ARCHIVE_TYPE)
        @echo "Compiling $<"
        $(LIBTOOL_LINK) $(CXX) $(TTOOL_LDFLAGS) $(LDFLAGS) -L../lib -L../findlib -o $@ $(BEXTOBJS) $(DLIB) $(FDLIBS) \
-       -lbacfind -lbaccfg -lbac -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS)
+          -lbacfind -lbaccfg -lbac -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS)
 
 bscan.o: bscan.c
        @echo "Compiling $<"
        $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(CPPFLAGS) -I$(srcdir) \
-       -I$(basedir) $(DINCLUDE) $(CFLAGS) $<
+          -I$(basedir) $(DINCLUDE) $(CFLAGS) $<
 
 bscan: Makefile $(SCNOBJS) ../findlib/libbacfind$(DEFAULT_ARCHIVE_TYPE) ../lib/libbaccfg$(DEFAULT_ARCHIVE_TYPE) ../cats/libbacsql$(DEFAULT_ARCHIVE_TYPE)
        $(LIBTOOL_LINK) $(CXX) $(TTOOL_LDFLAGS) $(LDFLAGS) -L../lib -L../cats -L../findlib -o $@ $(SCNOBJS) \
-       -lbacsql $(DB_LIBS) $(FDLIBS) -lbacfind -lbaccfg -lbac -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS)
+          -lbacsql $(DB_LIBS) $(FDLIBS) -lbacfind -lbaccfg -lbac -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS)
 
 bcopy.o: bcopy.c
        @echo "Compiling $<"
        $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(CPPFLAGS) -I$(srcdir) \
-       -I$(basedir) $(DINCLUDE) $(CFLAGS) $<
+          -I$(basedir) $(DINCLUDE) $(CFLAGS) $<
 
 bcopy: Makefile $(COPYOBJS) ../findlib/libbacfind$(DEFAULT_ARCHIVE_TYPE) ../lib/libbaccfg$(DEFAULT_ARCHIVE_TYPE) ../lib/libbac$(DEFAULT_ARCHIVE_TYPE)
        $(LIBTOOL_LINK) $(CXX) $(TTOOL_LDFLAGS) $(LDFLAGS) -L../lib -o $@ $(COPYOBJS) \
-       -lbaccfg -lbac -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS)
+          -lbaccfg -lbac -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS)
 
 Makefile: $(srcdir)/Makefile.in $(topdir)/config.status
        cd $(topdir) \
index af8e667f89d11b4b330be868a0f2f5e07fb8ae96..4ed9ef281f6afe6329460a00072912e91fd01aa6 100644 (file)
@@ -216,23 +216,13 @@ void    _lock_reservations();
 void    _unlock_reservations();
 void    _lock_volumes();
 void    _unlock_volumes();
-VOLRES *reserve_volume(DCR *dcr, const char *VolumeName);
-VOLRES *find_volume(const char *VolumeName);
-bool    free_volume(DEVICE *dev);
 void    unreserve_device(DCR *dcr);
-bool    volume_unused(DCR *dcr);
-void    create_volume_list();
-void    free_volume_list();
-void    list_volumes(void sendit(const char *msg, int len, void *sarg), void *arg);
-bool    is_volume_in_use(DCR *dcr);
 void    send_drive_reserve_messages(JCR *jcr, void sendit(const char *msg, int len, void *sarg), void *arg);
 bool    find_suitable_device_for_job(JCR *jcr, RCTX &rctx);
 int     search_res_for_device(RCTX &rctx);
 void    release_reserve_messages(JCR *jcr);
-void debug_list_volumes(const char *imsg);
 
 extern int reservations_lock_count;
-extern int vol_list_lock_count;
 
 #ifdef  SD_DEBUG_LOCK
 
@@ -271,9 +261,25 @@ extern int vol_list_lock_count;
 #define unlock_reservations() _unlock_reservations()
 #define lock_volumes() _lock_volumes()
 #define unlock_volumes() _unlock_volumes()
+bool    volume_unused(DCR *dcr);
+void    create_volume_list();
+void    free_volume_list();
+void    list_volumes(void sendit(const char *msg, int len, void *sarg), void *arg);
+bool    is_volume_in_use(DCR *dcr);
+void debug_list_volumes(const char *imsg);
+extern int vol_list_lock_count;
 
 #endif
 
+/* From vol_mgr.c */
+void    init_vol_list_lock();
+void    term_vol_list_lock();
+VOLRES *reserve_volume(DCR *dcr, const char *VolumeName);
+VOLRES *find_volume(const char *VolumeName);
+bool    free_volume(DEVICE *dev);
+bool    is_vol_list_empty();
+dlist  *dup_vol_list(JCR *jcr);
+void    free_temp_vol_list(dlist *temp_vol_list);
 
 
 /* From spool.c */
index b262b76b216c9534fa28a1045a30f09f1e419b77..b55da00227cfdeb52e0d758aa13cc2963339539f 100644 (file)
@@ -41,9 +41,7 @@
 
 const int dbglvl =  50;
 
-static dlist *vol_list = NULL;
 static brwlock_t reservation_lock;
-static brwlock_t vol_list_lock;
 
 /* Forward referenced functions */
 static int can_reserve_drive(DCR *dcr, RCTX &rctx);
@@ -78,11 +76,6 @@ bool use_cmd(JCR *jcr)
    return true;
 }
 
-static int my_compare(void *item1, void *item2)
-{
-   return strcmp(((VOLRES *)item1)->vol_name, ((VOLRES *)item2)->vol_name);
-}
-
 /*
  * This allows a given thread to recursively call lock_reservations.
  *   It must, of course, call unlock_... the same number of times.
@@ -96,17 +89,13 @@ void init_reservations_lock()
             be.bstrerror(errstat));
    }
 
-   if ((errstat=rwl_init(&vol_list_lock)) != 0) {
-      berrno be;
-      Emsg1(M_ABORT, 0, _("Unable to initialize volume list lock. ERR=%s\n"),
-            be.bstrerror(errstat));
-   }
+   init_vol_list_lock();
 }
 
 void term_reservations_lock()
 {
    rwl_destroy(&reservation_lock);
-   rwl_destroy(&vol_list_lock);
+   term_vol_list_lock();
 }
 
 int reservations_lock_count = 0;
@@ -134,347 +123,6 @@ void _unlock_reservations()
    }
 }
 
-int vol_list_lock_count = 0;
-
-/* 
- * This allows a given thread to recursively call to lock_volumes()
- */
-void _lock_volumes()
-{
-   int errstat;
-   vol_list_lock_count++;
-   if ((errstat=rwl_writelock(&vol_list_lock)) != 0) {
-      berrno be;
-      Emsg2(M_ABORT, 0, "rwl_writelock failure. stat=%d: ERR=%s\n",
-           errstat, be.bstrerror(errstat));
-   }
-}
-
-void _unlock_volumes()
-{
-   int errstat;
-   vol_list_lock_count--;
-   if ((errstat=rwl_writeunlock(&vol_list_lock)) != 0) {
-      berrno be;
-      Emsg2(M_ABORT, 0, "rwl_writeunlock failure. stat=%d: ERR=%s\n",
-           errstat, be.bstrerror(errstat));
-   }
-}
-
-
-/*
- * List Volumes -- this should be moved to status.c
- */
-enum {
-   debug_lock = true,
-   debug_nolock = false
-};
-
-void debug_list_volumes(const char *imsg)
-{
-   VOLRES *vol;
-   POOL_MEM msg(PM_MESSAGE);
-
-   lock_volumes();
-   foreach_dlist(vol, vol_list) {
-      if (vol->dev) {
-         Mmsg(msg, "List %s: %s in_use=%d on device %s\n", imsg, 
-              vol->vol_name, vol->is_in_use(), vol->dev->print_name());
-      } else {
-         Mmsg(msg, "List %s: %s in_use=%d no dev\n", imsg, vol->vol_name, 
-              vol->is_in_use());
-      }
-      Dmsg1(dbglvl, "%s", msg.c_str());
-   }
-
-   unlock_volumes();
-}
-
-
-/*
- * List Volumes -- this should be moved to status.c
- */
-void list_volumes(void sendit(const char *msg, int len, void *sarg), void *arg)
-{
-   VOLRES *vol;
-   POOL_MEM msg(PM_MESSAGE);
-   int len;
-
-   lock_volumes();
-   foreach_dlist(vol, vol_list) {
-      DEVICE *dev = vol->dev;
-      if (dev) {
-         len = Mmsg(msg, "%s on device %s\n", vol->vol_name, dev->print_name());
-         sendit(msg.c_str(), len, arg);
-         len = Mmsg(msg, "    Reader=%d writers=%d devres=%d volinuse=%d\n", 
-            dev->can_read()?1:0, dev->num_writers, dev->num_reserved(),   
-            vol->is_in_use());
-         sendit(msg.c_str(), len, arg);
-      } else {
-         len = Mmsg(msg, "%s no device. volinuse= %d\n", vol->vol_name, 
-            vol->is_in_use());
-         sendit(msg.c_str(), len, arg);
-      }
-   }
-   unlock_volumes();
-}
-
-/*
- * Create a Volume item to put in the Volume list
- *   Ensure that the device points to it.
- */
-static VOLRES *new_vol_item(DCR *dcr, const char *VolumeName)
-{
-   VOLRES *vol;
-   vol = (VOLRES *)malloc(sizeof(VOLRES));
-   memset(vol, 0, sizeof(VOLRES));
-   vol->vol_name = bstrdup(VolumeName);
-   vol->dev = dcr->dev;
-   Dmsg3(dbglvl, "new Vol=%s at %p dev=%s\n",
-         VolumeName, vol->vol_name, vol->dev->print_name());
-   return vol;
-}
-
-static void free_vol_item(VOLRES *vol)
-{
-   DEVICE *dev = NULL;
-
-   free(vol->vol_name);
-   if (vol->dev) {
-      dev = vol->dev;
-   }
-   free(vol);
-   if (dev) {
-      dev->vol = NULL;
-   }
-}
-
-/*
- * Put a new Volume entry in the Volume list. This
- *  effectively reserves the volume so that it will
- *  not be mounted again.
- *
- * If the device has any current volume associated with it,
- *  and it is a different Volume, and the device is not busy,
- *  we release the old Volume item and insert the new one.
- * 
- * It is assumed that the device is free and locked so that
- *  we can change the device structure.
- *
- * Some details of the Volume list handling:
- *
- *  1. The Volume list entry must be attached to the drive (rather than 
- *       attached to a job as it currently is. I.e. the drive that "owns" 
- *       the volume (in use, mounted)
- *       must point to the volume (still to be maintained in a list).
- *
- *  2. The Volume is entered in the list when a drive is reserved.  
- *
- *  3. When a drive is in use, the device code must appropriately update the
- *      volume name as it changes (currently the list is static -- an entry is
- *      removed when the Volume is no longer reserved, in use or mounted).  
- *      The new code must keep the same list entry as long as the drive
- *       has any volume associated with it but the volume name in the list
- *       must be updated when the drive has a different volume mounted.
- *
- *  4. A job that has reserved a volume, can un-reserve the volume, and if the 
- *      volume is not mounted, and not reserved, and not in use, it will be
- *      removed from the list.
- *
- *  5. If a job wants to reserve a drive with a different Volume from the one on
- *      the drive, it can re-use the drive for the new Volume.
- *
- *  6. If a job wants a Volume that is in a different drive, it can either use the
- *      other drive or take the volume, only if the other drive is not in use or
- *      not reserved.
- *
- *  One nice aspect of this is that the reserve use count and the writer use count 
- *  already exist and are correctly programmed and will need no changes -- use 
- *  counts are always very tricky.
- *
- *  The old code had a concept of "reserving" a Volume, but was changed 
- *  to reserving and using a drive.  A volume is must be attached to (owned by) a 
- *  drive and can move from drive to drive or be unused given certain specific 
- *  conditions of the drive.  The key is that the drive must "own" the Volume.  
- *  The old code had the job (dcr) owning the volume (more or less).  The job was
- *  to change the insertion and removal of the volumes from the list to be based 
- *  on the drive rather than the job.  
- *
- *  Return: VOLRES entry on success
- *          NULL volume busy on another drive
- */
-VOLRES *reserve_volume(DCR *dcr, const char *VolumeName)
-{
-   VOLRES *vol, *nvol;
-   DEVICE * volatile dev = dcr->dev;
-
-   ASSERT(dev != NULL);
-
-   Dmsg2(dbglvl, "enter reserve_volume=%s drive=%s\n", VolumeName, 
-      dcr->dev->print_name());
-   /* 
-    * We lock the reservations system here to ensure
-    *  when adding a new volume that no newly scheduled
-    *  job can reserve it.
-    */
-   lock_volumes();
-   debug_list_volumes("begin reserve_volume");
-   /* 
-    * First, remove any old volume attached to this device as it
-    *  is no longer used.
-    */
-   if (dev->vol) {
-      vol = dev->vol;
-      Dmsg4(dbglvl, "Vol attached=%s, newvol=%s volinuse=%d on %s\n",
-         vol->vol_name, VolumeName, vol->is_in_use(), dev->print_name());
-      /*
-       * Make sure we don't remove the current volume we are inserting
-       *  because it was probably inserted by another job, or it
-       *  is not being used and is marked as not reserved.
-       */
-      if (strcmp(vol->vol_name, VolumeName) == 0) {
-         Dmsg2(dbglvl, "=== set reserved vol=%s dev=%s\n", VolumeName,
-               vol->dev->print_name());
-         goto get_out;                  /* Volume already on this device */
-      } else {
-         /* Don't release a volume if it was reserved by someone other than us */
-         if (vol->is_in_use() && !dcr->reserved_volume) { 
-            Dmsg1(dbglvl, "Cannot free vol=%s. It is reserved.\n", vol->vol_name);
-            vol = NULL;                  /* vol in use */
-            goto get_out;
-         }
-         Dmsg2(dbglvl, "reserve_vol free vol=%s at %p\n", vol->vol_name, vol->vol_name);
-         free_volume(dev);
-         Dmsg0(50, "set_unload\n");
-         dev->set_unload();             /* have to unload current volume */
-         debug_list_volumes("reserve_vol free");
-      }
-   }
-
-   /* Create a new Volume entry */
-   nvol = new_vol_item(dcr, VolumeName);
-
-   /*
-    * Now try to insert the new Volume
-    */
-   vol = (VOLRES *)vol_list->binary_insert(nvol, my_compare);
-   if (vol != nvol) {
-      Dmsg2(dbglvl, "Found vol=%s dev-same=%d\n", vol->vol_name, dev==vol->dev);
-      /*
-       * At this point, a Volume with this name already is in the list,
-       *   so we simply release our new Volume entry. Note, this should
-       *   only happen if we are moving the volume from one drive to another.
-       */
-      Dmsg2(dbglvl, "reserve_vol free-tmp vol=%s at %p\n", 
-            vol->vol_name, vol->vol_name);
-      /*
-       * Clear dev pointer so that free_vol_item() doesn't 
-       *  take away our volume. 
-       */
-      nvol->dev = NULL;                  /* don't zap dev entry */
-      free_vol_item(nvol);
-
-      /*
-       * Check if we are trying to use the Volume on a different drive
-       *  dev      is our device
-       *  vol->dev is where the Volume we want is
-       */
-      if (dev != vol->dev) {
-         /* Caller wants to switch Volume to another device */
-         if (!vol->dev->is_busy() && !vol->is_swapping()) {
-            int32_t slot;
-            Dmsg3(dbglvl, "==== Swap vol=%s from dev=%s to %s\n", 
-               VolumeName, vol->dev->print_name(), dev->print_name());
-            free_volume(dev);            /* free any volume attached to our drive */
-            Dmsg0(50, "set_unload\n");
-            dev->set_unload();           /* Unload any volume that is on our drive */
-            dcr->dev = vol->dev;         /* temp point to other dev */
-            slot = get_autochanger_loaded_slot(dcr);  /* get slot on other drive */
-            dcr->dev = dev;              /* restore dev */
-            vol->set_slot(slot);         /* save slot */
-            vol->dev->set_unload();      /* unload the other drive */
-            vol->set_swapping();         /* swap from other drive */
-            dev->swap_dev = vol->dev;    /* remember to get this vol */
-            dev->set_load();             /* then reload on our drive */
-            vol->dev->vol = NULL;        /* remove volume from other drive */
-            vol->dev = dev;              /* point the Volume at our drive */
-            dev->vol = vol;              /* point our drive at the Volume */
-         } else {
-            Dmsg3(dbglvl, "==== Swap not possible Vol busy vol=%s from dev=%s to %s\n", 
-               VolumeName, vol->dev->print_name(), dev->print_name());
-            vol = NULL;                  /* device busy */
-            goto get_out;
-         }
-      } else {
-         dev->vol = vol;
-      }
-   } else {
-      dev->vol = vol;                    /* point to newly inserted volume */
-   }
-
-get_out:
-   if (vol) {
-      Dmsg2(dbglvl, "=== set in_use. vol=%s dev=%s\n", vol->vol_name,
-            vol->dev->print_name());
-      vol->set_in_use();
-      dcr->reserved_volume = true;
-      bstrncpy(dcr->VolumeName, vol->vol_name, sizeof(dcr->VolumeName));
-   }
-   debug_list_volumes("end new volume");
-   unlock_volumes();
-   return vol;
-}
-
-/* 
- * Switch from current device to given device  
- *   (not yet used) 
- */
-#ifdef xxx
-void switch_device(DCR *dcr, DEVICE *dev)
-{
-   DCR save_dcr;
-
-   dev->dlock();
-   memcpy(&save_dcr, dcr, sizeof(save_dcr));
-   clean_device(dcr);                  /* clean up the dcr */
-
-   dcr->dev = dev;                     /* get new device pointer */
-   Jmsg(dcr->jcr, M_INFO, 0, _("Device switch. New device %s chosen.\n"),
-      dcr->dev->print_name());
-
-   bstrncpy(dcr->VolumeName, save_dcr.VolumeName, sizeof(dcr->VolumeName));
-   bstrncpy(dcr->media_type, save_dcr.media_type, sizeof(dcr->media_type));
-   dcr->VolCatInfo.Slot = save_dcr.VolCatInfo.Slot;
-   bstrncpy(dcr->pool_name, save_dcr.pool_name, sizeof(dcr->pool_name));
-   bstrncpy(dcr->pool_type, save_dcr.pool_type, sizeof(dcr->pool_type));
-   bstrncpy(dcr->dev_name, dev->dev_name, sizeof(dcr->dev_name));
-
-// dcr->set_reserved();
-
-   dev->dunlock();
-}
-#endif
-
-/*
- * Search for a Volume name in the Volume list.
- *
- *  Returns: VOLRES entry on success
- *           NULL if the Volume is not in the list
- */
-VOLRES *find_volume(const char *VolumeName) 
-{
-   VOLRES vol, *fvol;
-   /* Do not lock reservations here */
-   lock_volumes();
-   vol.vol_name = bstrdup(VolumeName);
-   fvol = (VOLRES *)vol_list->binary_search(&vol, my_compare);
-   free(vol.vol_name);
-   Dmsg2(dbglvl, "find_vol=%s found=%d\n", VolumeName, fvol!=NULL);
-   debug_list_volumes("find_volume");
-   unlock_volumes();
-   return fvol;
-}
-
 void DCR::set_reserved()
 {
    m_reserved = true;
@@ -516,149 +164,6 @@ void DCR::unreserve_device()
    unlock_volumes();
 }
 
-/*  
- * Free a Volume from the Volume list if it is no longer used
- *   Note, for tape drives we want to remember where the Volume
- *   was when last used, so rather than free the volume entry,
- *   we simply mark it "not reserved" so when the drive is really
- *   needed for another volume, we can reuse it.
- *
- *  Returns: true if the Volume found and "removed" from the list
- *           false if the Volume is not in the list or is in use
- */
-bool volume_unused(DCR *dcr)
-{
-   DEVICE *dev = dcr->dev;
-
-   if (!dev->vol) {
-      Dmsg1(dbglvl, "vol_unused: no vol on %s\n", dev->print_name());
-      debug_list_volumes("null vol cannot unreserve_volume");
-      return false;
-   }
-   if (dev->vol->is_swapping()) {
-      Dmsg1(dbglvl, "vol_unused: vol being swapped on %s\n", dev->print_name());
-      Dmsg1(dbglvl, "=== clear in_use vol=%s\n", dev->vol->vol_name);
-      dev->vol->clear_in_use();
-      debug_list_volumes("swapping vol cannot free_volume");
-      return false;
-   }
-
-   /*  
-    * If this is a tape, we do not free the volume, rather we wait
-    *  until the autoloader unloads it, or until another tape is
-    *  explicitly read in this drive. This allows the SD to remember
-    *  where the tapes are or last were.
-    */
-   Dmsg4(dbglvl, "=== set not reserved vol=%s num_writers=%d dev_reserved=%d dev=%s\n",
-      dev->vol->vol_name, dev->num_writers, dev->num_reserved(), dev->print_name());
-   Dmsg1(dbglvl, "=== clear in_use vol=%s\n", dev->vol->vol_name);
-   dev->vol->clear_in_use();
-   if (dev->is_tape() || dev->is_autochanger()) {
-      return true;
-   } else {
-      /*
-       * Note, this frees the volume reservation entry, but the 
-       *   file descriptor remains open with the OS.
-       */
-      return free_volume(dev);
-   }
-}
-
-/*
- * Unconditionally release the volume entry
- */
-bool free_volume(DEVICE *dev)
-{
-   VOLRES *vol;
-
-   if (dev->vol == NULL) {
-      Dmsg1(dbglvl, "No vol on dev %s\n", dev->print_name());
-      return false;
-   }
-   lock_volumes();
-   vol = dev->vol;
-   /* Don't free a volume while it is being swapped */
-   if (!vol->is_swapping()) {
-      Dmsg1(dbglvl, "=== clear in_use vol=%s\n", dev->vol->vol_name);
-      dev->vol = NULL;
-      vol_list->remove(vol);
-      Dmsg2(dbglvl, "=== remove volume %s dev=%s\n", vol->vol_name, dev->print_name());
-      free_vol_item(vol);
-      debug_list_volumes("free_volume");
-   }
-   unlock_volumes();
-   return true;
-}
-
-      
-/* Create the Volume list */
-void create_volume_list()
-{
-   VOLRES *vol = NULL;
-   if (vol_list == NULL) {
-      vol_list = New(dlist(vol, &vol->link));
-   }
-}
-
-/* Release all Volumes from the list */
-void free_volume_list()
-{
-   VOLRES *vol;
-   if (!vol_list) {
-      return;
-   }
-   lock_volumes();
-   foreach_dlist(vol, vol_list) {
-      if (vol->dev) {
-         Dmsg2(dbglvl, "free vol_list Volume=%s dev=%s\n", vol->vol_name, vol->dev->print_name());
-      } else {
-         Dmsg1(dbglvl, "free vol_list Volume=%s No dev\n", vol->vol_name);
-      }
-      free(vol->vol_name);
-      vol->vol_name = NULL;
-   }
-   delete vol_list;
-   vol_list = NULL;
-   unlock_volumes();
-}
-
-bool DCR::can_i_use_volume()
-{
-   bool rtn = true;
-   VOLRES *vol;
-
-   lock_volumes();
-   vol = find_volume(VolumeName);
-   if (!vol) {
-      Dmsg1(dbglvl, "Vol=%s not in use.\n", VolumeName);
-      goto get_out;                   /* vol not in list */
-   }
-   ASSERT(vol->dev != NULL);
-
-   if (dev == vol->dev) {        /* same device OK */
-      Dmsg1(dbglvl, "Vol=%s on same dev.\n", VolumeName);
-      goto get_out;
-   } else {
-      Dmsg3(dbglvl, "Vol=%s on %s we have %s\n", VolumeName,
-            vol->dev->print_name(), dev->print_name());
-   }
-   /* ***FIXME*** check this ... */
-   if (!vol->dev->is_busy()) {
-      Dmsg2(dbglvl, "Vol=%s dev=%s not busy.\n", VolumeName, vol->dev->print_name());
-      goto get_out;
-   } else {
-      Dmsg2(dbglvl, "Vol=%s dev=%s busy.\n", VolumeName, vol->dev->print_name());
-   }
-   Dmsg2(dbglvl, "Vol=%s in use by %s.\n", VolumeName, vol->dev->print_name());
-   rtn = false;
-
-get_out:
-   unlock_volumes();
-   return rtn;
-
-}
-
-
 /*
  * We get the following type of information:
  *
@@ -919,39 +424,10 @@ bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx)
     *  force try a mounted drive because they are all busy), we
     *  start by looking at all the Volumes in the volume list.
     */
-   if (!vol_list->empty() && rctx.append && rctx.PreferMountedVols) {
-      dlist *temp_vol_list, *save_vol_list;
+   if (!is_vol_list_empty() && rctx.append && rctx.PreferMountedVols) {
+      dlist *temp_vol_list;
       VOLRES *vol = NULL;
-      lock_volumes();
-      Dmsg0(dbglvl, "lock volumes\n");                           
-
-      /*  
-       * Create a temporary copy of the volume list.  We do this,
-       *   to avoid having the volume list locked during the
-       *   call to reserve_device(), which would cause a deadlock.
-       * Note, we may want to add an update counter on the vol_list
-       *   so that if it is modified while we are traversing the copy
-       *   we can take note and act accordingly (probably redo the 
-       *   search at least a few times).
-       */
-      Dmsg0(dbglvl, "duplicate vol list\n");
-      temp_vol_list = New(dlist(vol, &vol->link));
-      foreach_dlist(vol, vol_list) {
-         VOLRES *nvol;
-         VOLRES *tvol = (VOLRES *)malloc(sizeof(VOLRES));
-         memset(tvol, 0, sizeof(VOLRES));
-         tvol->vol_name = bstrdup(vol->vol_name);
-         tvol->dev = vol->dev;
-         nvol = (VOLRES *)temp_vol_list->binary_insert(tvol, my_compare);
-         if (tvol != nvol) {
-            tvol->dev = NULL;                   /* don't zap dev entry */
-            free_vol_item(tvol);
-            Pmsg0(000, "Logic error. Duplicating vol list hit duplicate.\n");
-            Jmsg(jcr, M_WARNING, 0, "Logic error. Duplicating vol list hit duplicate.\n");
-         }
-      }
-      Dmsg0(dbglvl, "unlock volumes\n");
-      unlock_volumes();
+      temp_vol_list = dup_vol_list(jcr);
 
       /* Look through reserved volumes for one we can use */
       Dmsg0(dbglvl, "look for vol in vol list\n");
@@ -1014,15 +490,7 @@ bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx)
       } /* end for loop over reserved volumes */
 
       Dmsg0(dbglvl, "lock volumes\n");
-      lock_volumes();
-      save_vol_list = vol_list;
-      vol_list = temp_vol_list;
-      free_volume_list();                  /* release temp_vol_list */
-      vol_list = save_vol_list;
-      Dmsg0(dbglvl, "deleted temp vol list\n");
-      Dmsg0(dbglvl, "unlock volumes\n");
-      unlock_volumes();
-      debug_list_volumes("after free temp table");
+      free_temp_vol_list(temp_vol_list);
    }
    if (ok) {
       Dmsg1(dbglvl, "OK dev found. Vol=%s from in-use vols list\n", rctx.VolumeName);
diff --git a/bacula/src/stored/vol_mgr.c b/bacula/src/stored/vol_mgr.c
new file mode 100644 (file)
index 0000000..17dba76
--- /dev/null
@@ -0,0 +1,612 @@
+/*
+   Bacula® - The Network Backup Solution
+
+   Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
+
+   The main author of Bacula is Kern Sibbald, with contributions from
+   many others, a complete list can be found in the file AUTHORS.
+   This program is Free Software; you can redistribute it and/or
+   modify it under the terms of version two of the GNU General Public
+   License as published by the Free Software Foundation and included
+   in the file LICENSE.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
+
+   Bacula® is a registered trademark of Kern Sibbald.
+   The licensor of Bacula is the Free Software Foundation Europe
+   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+   Switzerland, email:ftf@fsfeurope.org.
+*/
+/*
+ *   Volume management functions for Storage Daemon
+ *
+ *   Kern Sibbald, MM
+ *
+ *   Split from reserve.c October 2008
+ *
+ *   Version $Id: reserve.c 7380 2008-07-14 10:42:59Z kerns $
+ *
+ */
+
+#include "bacula.h"
+#include "stored.h"
+
+const int dbglvl =  50;
+
+static dlist *vol_list = NULL;
+static brwlock_t vol_list_lock;
+
+/* Forward referenced functions */
+static void free_vol_item(VOLRES *vol);
+
+
+static int my_compare(void *item1, void *item2)
+{
+   return strcmp(((VOLRES *)item1)->vol_name, ((VOLRES *)item2)->vol_name);
+}
+
+bool is_vol_list_empty() 
+{
+   return vol_list->empty();
+}
+
+int vol_list_lock_count = 0;
+
+void init_vol_list_lock()
+{
+   int errstat;
+   if ((errstat=rwl_init(&vol_list_lock)) != 0) {
+      berrno be;
+      Emsg1(M_ABORT, 0, _("Unable to initialize volume list lock. ERR=%s\n"),
+            be.bstrerror(errstat));
+   }
+}
+
+void term_vol_list_lock()
+{
+   rwl_destroy(&vol_list_lock);
+}
+
+
+
+/* 
+ * This allows a given thread to recursively call to lock_volumes()
+ */
+void _lock_volumes()
+{
+   int errstat;
+   vol_list_lock_count++;
+   if ((errstat=rwl_writelock(&vol_list_lock)) != 0) {
+      berrno be;
+      Emsg2(M_ABORT, 0, "rwl_writelock failure. stat=%d: ERR=%s\n",
+           errstat, be.bstrerror(errstat));
+   }
+}
+
+void _unlock_volumes()
+{
+   int errstat;
+   vol_list_lock_count--;
+   if ((errstat=rwl_writeunlock(&vol_list_lock)) != 0) {
+      berrno be;
+      Emsg2(M_ABORT, 0, "rwl_writeunlock failure. stat=%d: ERR=%s\n",
+           errstat, be.bstrerror(errstat));
+   }
+}
+
+dlist *dup_vol_list(JCR *jcr)
+{
+   dlist *temp_vol_list;
+   VOLRES *vol = NULL;
+
+   lock_volumes();
+   Dmsg0(dbglvl, "lock volumes\n");                           
+
+   /*  
+    * Create a temporary copy of the volume list.  We do this,
+    *   to avoid having the volume list locked during the
+    *   call to reserve_device(), which would cause a deadlock.
+    * Note, we may want to add an update counter on the vol_list
+    *   so that if it is modified while we are traversing the copy
+    *   we can take note and act accordingly (probably redo the 
+    *   search at least a few times).
+    */
+   Dmsg0(dbglvl, "duplicate vol list\n");
+   temp_vol_list = New(dlist(vol, &vol->link));
+   foreach_dlist(vol, vol_list) {
+      VOLRES *nvol;
+      VOLRES *tvol = (VOLRES *)malloc(sizeof(VOLRES));
+      memset(tvol, 0, sizeof(VOLRES));
+      tvol->vol_name = bstrdup(vol->vol_name);
+      tvol->dev = vol->dev;
+      nvol = (VOLRES *)temp_vol_list->binary_insert(tvol, my_compare);
+      if (tvol != nvol) {
+         tvol->dev = NULL;                   /* don't zap dev entry */
+         free_vol_item(tvol);
+         Pmsg0(000, "Logic error. Duplicating vol list hit duplicate.\n");
+         Jmsg(jcr, M_WARNING, 0, "Logic error. Duplicating vol list hit duplicate.\n");
+      }
+   }
+   Dmsg0(dbglvl, "unlock volumes\n");
+   unlock_volumes();
+   return temp_vol_list;
+}
+
+void free_temp_vol_list(dlist *temp_vol_list)
+{
+   dlist *save_vol_list;
+   
+   lock_volumes();
+   save_vol_list = vol_list;
+   vol_list = temp_vol_list;
+   free_volume_list();                  /* release temp_vol_list */
+   vol_list = save_vol_list;
+   Dmsg0(dbglvl, "deleted temp vol list\n");
+   Dmsg0(dbglvl, "unlock volumes\n");
+   unlock_volumes();
+   debug_list_volumes("after free temp table");
+}
+
+
+/*
+ * List Volumes -- this should be moved to status.c
+ */
+enum {
+   debug_lock = true,
+   debug_nolock = false
+};
+
+void debug_list_volumes(const char *imsg)
+{
+   VOLRES *vol;
+   POOL_MEM msg(PM_MESSAGE);
+
+   lock_volumes();
+   foreach_dlist(vol, vol_list) {
+      if (vol->dev) {
+         Mmsg(msg, "List %s: %s in_use=%d on device %s\n", imsg, 
+              vol->vol_name, vol->is_in_use(), vol->dev->print_name());
+      } else {
+         Mmsg(msg, "List %s: %s in_use=%d no dev\n", imsg, vol->vol_name, 
+              vol->is_in_use());
+      }
+      Dmsg1(dbglvl, "%s", msg.c_str());
+   }
+
+   unlock_volumes();
+}
+
+
+/*
+ * List Volumes -- this should be moved to status.c
+ */
+void list_volumes(void sendit(const char *msg, int len, void *sarg), void *arg)
+{
+   VOLRES *vol;
+   POOL_MEM msg(PM_MESSAGE);
+   int len;
+
+   lock_volumes();
+   foreach_dlist(vol, vol_list) {
+      DEVICE *dev = vol->dev;
+      if (dev) {
+         len = Mmsg(msg, "%s on device %s\n", vol->vol_name, dev->print_name());
+         sendit(msg.c_str(), len, arg);
+         len = Mmsg(msg, "    Reader=%d writers=%d devres=%d volinuse=%d\n", 
+            dev->can_read()?1:0, dev->num_writers, dev->num_reserved(),   
+            vol->is_in_use());
+         sendit(msg.c_str(), len, arg);
+      } else {
+         len = Mmsg(msg, "%s no device. volinuse= %d\n", vol->vol_name, 
+            vol->is_in_use());
+         sendit(msg.c_str(), len, arg);
+      }
+   }
+   unlock_volumes();
+}
+
+/*
+ * Create a Volume item to put in the Volume list
+ *   Ensure that the device points to it.
+ */
+static VOLRES *new_vol_item(DCR *dcr, const char *VolumeName)
+{
+   VOLRES *vol;
+   vol = (VOLRES *)malloc(sizeof(VOLRES));
+   memset(vol, 0, sizeof(VOLRES));
+   vol->vol_name = bstrdup(VolumeName);
+   vol->dev = dcr->dev;
+   Dmsg3(dbglvl, "new Vol=%s at %p dev=%s\n",
+         VolumeName, vol->vol_name, vol->dev->print_name());
+   return vol;
+}
+
+static void free_vol_item(VOLRES *vol)
+{
+   DEVICE *dev = NULL;
+
+   free(vol->vol_name);
+   if (vol->dev) {
+      dev = vol->dev;
+   }
+   free(vol);
+   if (dev) {
+      dev->vol = NULL;
+   }
+}
+
+/*
+ * Put a new Volume entry in the Volume list. This
+ *  effectively reserves the volume so that it will
+ *  not be mounted again.
+ *
+ * If the device has any current volume associated with it,
+ *  and it is a different Volume, and the device is not busy,
+ *  we release the old Volume item and insert the new one.
+ * 
+ * It is assumed that the device is free and locked so that
+ *  we can change the device structure.
+ *
+ * Some details of the Volume list handling:
+ *
+ *  1. The Volume list entry must be attached to the drive (rather than 
+ *       attached to a job as it currently is. I.e. the drive that "owns" 
+ *       the volume (in use, mounted)
+ *       must point to the volume (still to be maintained in a list).
+ *
+ *  2. The Volume is entered in the list when a drive is reserved.  
+ *
+ *  3. When a drive is in use, the device code must appropriately update the
+ *      volume name as it changes (currently the list is static -- an entry is
+ *      removed when the Volume is no longer reserved, in use or mounted).  
+ *      The new code must keep the same list entry as long as the drive
+ *       has any volume associated with it but the volume name in the list
+ *       must be updated when the drive has a different volume mounted.
+ *
+ *  4. A job that has reserved a volume, can un-reserve the volume, and if the 
+ *      volume is not mounted, and not reserved, and not in use, it will be
+ *      removed from the list.
+ *
+ *  5. If a job wants to reserve a drive with a different Volume from the one on
+ *      the drive, it can re-use the drive for the new Volume.
+ *
+ *  6. If a job wants a Volume that is in a different drive, it can either use the
+ *      other drive or take the volume, only if the other drive is not in use or
+ *      not reserved.
+ *
+ *  One nice aspect of this is that the reserve use count and the writer use count 
+ *  already exist and are correctly programmed and will need no changes -- use 
+ *  counts are always very tricky.
+ *
+ *  The old code had a concept of "reserving" a Volume, but was changed 
+ *  to reserving and using a drive.  A volume is must be attached to (owned by) a 
+ *  drive and can move from drive to drive or be unused given certain specific 
+ *  conditions of the drive.  The key is that the drive must "own" the Volume.  
+ *  The old code had the job (dcr) owning the volume (more or less).  The job was
+ *  to change the insertion and removal of the volumes from the list to be based 
+ *  on the drive rather than the job.  
+ *
+ *  Return: VOLRES entry on success
+ *          NULL volume busy on another drive
+ */
+VOLRES *reserve_volume(DCR *dcr, const char *VolumeName)
+{
+   VOLRES *vol, *nvol;
+   DEVICE * volatile dev = dcr->dev;
+
+   ASSERT(dev != NULL);
+
+   Dmsg2(dbglvl, "enter reserve_volume=%s drive=%s\n", VolumeName, 
+      dcr->dev->print_name());
+   /* 
+    * We lock the reservations system here to ensure
+    *  when adding a new volume that no newly scheduled
+    *  job can reserve it.
+    */
+   lock_volumes();
+   debug_list_volumes("begin reserve_volume");
+   /* 
+    * First, remove any old volume attached to this device as it
+    *  is no longer used.
+    */
+   if (dev->vol) {
+      vol = dev->vol;
+      Dmsg4(dbglvl, "Vol attached=%s, newvol=%s volinuse=%d on %s\n",
+         vol->vol_name, VolumeName, vol->is_in_use(), dev->print_name());
+      /*
+       * Make sure we don't remove the current volume we are inserting
+       *  because it was probably inserted by another job, or it
+       *  is not being used and is marked as not reserved.
+       */
+      if (strcmp(vol->vol_name, VolumeName) == 0) {
+         Dmsg2(dbglvl, "=== set reserved vol=%s dev=%s\n", VolumeName,
+               vol->dev->print_name());
+         goto get_out;                  /* Volume already on this device */
+      } else {
+         /* Don't release a volume if it was reserved by someone other than us */
+         if (vol->is_in_use() && !dcr->reserved_volume) { 
+            Dmsg1(dbglvl, "Cannot free vol=%s. It is reserved.\n", vol->vol_name);
+            vol = NULL;                  /* vol in use */
+            goto get_out;
+         }
+         Dmsg2(dbglvl, "reserve_vol free vol=%s at %p\n", vol->vol_name, vol->vol_name);
+         free_volume(dev);
+         Dmsg0(50, "set_unload\n");
+         dev->set_unload();             /* have to unload current volume */
+         debug_list_volumes("reserve_vol free");
+      }
+   }
+
+   /* Create a new Volume entry */
+   nvol = new_vol_item(dcr, VolumeName);
+
+   /*
+    * Now try to insert the new Volume
+    */
+   vol = (VOLRES *)vol_list->binary_insert(nvol, my_compare);
+   if (vol != nvol) {
+      Dmsg2(dbglvl, "Found vol=%s dev-same=%d\n", vol->vol_name, dev==vol->dev);
+      /*
+       * At this point, a Volume with this name already is in the list,
+       *   so we simply release our new Volume entry. Note, this should
+       *   only happen if we are moving the volume from one drive to another.
+       */
+      Dmsg2(dbglvl, "reserve_vol free-tmp vol=%s at %p\n", 
+            vol->vol_name, vol->vol_name);
+      /*
+       * Clear dev pointer so that free_vol_item() doesn't 
+       *  take away our volume. 
+       */
+      nvol->dev = NULL;                  /* don't zap dev entry */
+      free_vol_item(nvol);
+
+      /*
+       * Check if we are trying to use the Volume on a different drive
+       *  dev      is our device
+       *  vol->dev is where the Volume we want is
+       */
+      if (dev != vol->dev) {
+         /* Caller wants to switch Volume to another device */
+         if (!vol->dev->is_busy() && !vol->is_swapping()) {
+            int32_t slot;
+            Dmsg3(dbglvl, "==== Swap vol=%s from dev=%s to %s\n", 
+               VolumeName, vol->dev->print_name(), dev->print_name());
+            free_volume(dev);            /* free any volume attached to our drive */
+            Dmsg0(50, "set_unload\n");
+            dev->set_unload();           /* Unload any volume that is on our drive */
+            dcr->dev = vol->dev;         /* temp point to other dev */
+            slot = get_autochanger_loaded_slot(dcr);  /* get slot on other drive */
+            dcr->dev = dev;              /* restore dev */
+            vol->set_slot(slot);         /* save slot */
+            vol->dev->set_unload();      /* unload the other drive */
+            vol->set_swapping();         /* swap from other drive */
+            dev->swap_dev = vol->dev;    /* remember to get this vol */
+            dev->set_load();             /* then reload on our drive */
+            vol->dev->vol = NULL;        /* remove volume from other drive */
+            vol->dev = dev;              /* point the Volume at our drive */
+            dev->vol = vol;              /* point our drive at the Volume */
+         } else {
+            Dmsg3(dbglvl, "==== Swap not possible Vol busy vol=%s from dev=%s to %s\n", 
+               VolumeName, vol->dev->print_name(), dev->print_name());
+            vol = NULL;                  /* device busy */
+            goto get_out;
+         }
+      } else {
+         dev->vol = vol;
+      }
+   } else {
+      dev->vol = vol;                    /* point to newly inserted volume */
+   }
+
+get_out:
+   if (vol) {
+      Dmsg2(dbglvl, "=== set in_use. vol=%s dev=%s\n", vol->vol_name,
+            vol->dev->print_name());
+      vol->set_in_use();
+      dcr->reserved_volume = true;
+      bstrncpy(dcr->VolumeName, vol->vol_name, sizeof(dcr->VolumeName));
+   }
+   debug_list_volumes("end new volume");
+   unlock_volumes();
+   return vol;
+}
+
+/* 
+ * Switch from current device to given device  
+ *   (not yet used) 
+ */
+#ifdef xxx
+void switch_device(DCR *dcr, DEVICE *dev)
+{
+   DCR save_dcr;
+
+   dev->dlock();
+   memcpy(&save_dcr, dcr, sizeof(save_dcr));
+   clean_device(dcr);                  /* clean up the dcr */
+
+   dcr->dev = dev;                     /* get new device pointer */
+   Jmsg(dcr->jcr, M_INFO, 0, _("Device switch. New device %s chosen.\n"),
+      dcr->dev->print_name());
+
+   bstrncpy(dcr->VolumeName, save_dcr.VolumeName, sizeof(dcr->VolumeName));
+   bstrncpy(dcr->media_type, save_dcr.media_type, sizeof(dcr->media_type));
+   dcr->VolCatInfo.Slot = save_dcr.VolCatInfo.Slot;
+   bstrncpy(dcr->pool_name, save_dcr.pool_name, sizeof(dcr->pool_name));
+   bstrncpy(dcr->pool_type, save_dcr.pool_type, sizeof(dcr->pool_type));
+   bstrncpy(dcr->dev_name, dev->dev_name, sizeof(dcr->dev_name));
+
+// dcr->set_reserved();
+
+   dev->dunlock();
+}
+#endif
+
+/*
+ * Search for a Volume name in the Volume list.
+ *
+ *  Returns: VOLRES entry on success
+ *           NULL if the Volume is not in the list
+ */
+VOLRES *find_volume(const char *VolumeName) 
+{
+   VOLRES vol, *fvol;
+   /* Do not lock reservations here */
+   lock_volumes();
+   vol.vol_name = bstrdup(VolumeName);
+   fvol = (VOLRES *)vol_list->binary_search(&vol, my_compare);
+   free(vol.vol_name);
+   Dmsg2(dbglvl, "find_vol=%s found=%d\n", VolumeName, fvol!=NULL);
+   debug_list_volumes("find_volume");
+   unlock_volumes();
+   return fvol;
+}
+
+/*  
+ * Free a Volume from the Volume list if it is no longer used
+ *   Note, for tape drives we want to remember where the Volume
+ *   was when last used, so rather than free the volume entry,
+ *   we simply mark it "not reserved" so when the drive is really
+ *   needed for another volume, we can reuse it.
+ *
+ *  Returns: true if the Volume found and "removed" from the list
+ *           false if the Volume is not in the list or is in use
+ */
+bool volume_unused(DCR *dcr)
+{
+   DEVICE *dev = dcr->dev;
+
+   if (!dev->vol) {
+      Dmsg1(dbglvl, "vol_unused: no vol on %s\n", dev->print_name());
+      debug_list_volumes("null vol cannot unreserve_volume");
+      return false;
+   }
+   if (dev->vol->is_swapping()) {
+      Dmsg1(dbglvl, "vol_unused: vol being swapped on %s\n", dev->print_name());
+      Dmsg1(dbglvl, "=== clear in_use vol=%s\n", dev->vol->vol_name);
+      dev->vol->clear_in_use();
+      debug_list_volumes("swapping vol cannot free_volume");
+      return false;
+   }
+
+   /*  
+    * If this is a tape, we do not free the volume, rather we wait
+    *  until the autoloader unloads it, or until another tape is
+    *  explicitly read in this drive. This allows the SD to remember
+    *  where the tapes are or last were.
+    */
+   Dmsg4(dbglvl, "=== set not reserved vol=%s num_writers=%d dev_reserved=%d dev=%s\n",
+      dev->vol->vol_name, dev->num_writers, dev->num_reserved(), dev->print_name());
+   Dmsg1(dbglvl, "=== clear in_use vol=%s\n", dev->vol->vol_name);
+   dev->vol->clear_in_use();
+   if (dev->is_tape() || dev->is_autochanger()) {
+      return true;
+   } else {
+      /*
+       * Note, this frees the volume reservation entry, but the 
+       *   file descriptor remains open with the OS.
+       */
+      return free_volume(dev);
+   }
+}
+
+/*
+ * Unconditionally release the volume entry
+ */
+bool free_volume(DEVICE *dev)
+{
+   VOLRES *vol;
+
+   if (dev->vol == NULL) {
+      Dmsg1(dbglvl, "No vol on dev %s\n", dev->print_name());
+      return false;
+   }
+   lock_volumes();
+   vol = dev->vol;
+   /* Don't free a volume while it is being swapped */
+   if (!vol->is_swapping()) {
+      Dmsg1(dbglvl, "=== clear in_use vol=%s\n", dev->vol->vol_name);
+      dev->vol = NULL;
+      vol_list->remove(vol);
+      Dmsg2(dbglvl, "=== remove volume %s dev=%s\n", vol->vol_name, dev->print_name());
+      free_vol_item(vol);
+      debug_list_volumes("free_volume");
+   }
+   unlock_volumes();
+   return true;
+}
+
+      
+/* Create the Volume list */
+void create_volume_list()
+{
+   VOLRES *vol = NULL;
+   if (vol_list == NULL) {
+      vol_list = New(dlist(vol, &vol->link));
+   }
+}
+
+/* Release all Volumes from the list */
+void free_volume_list()
+{
+   VOLRES *vol;
+   if (!vol_list) {
+      return;
+   }
+   lock_volumes();
+   foreach_dlist(vol, vol_list) {
+      if (vol->dev) {
+         Dmsg2(dbglvl, "free vol_list Volume=%s dev=%s\n", vol->vol_name, vol->dev->print_name());
+      } else {
+         Dmsg1(dbglvl, "free vol_list Volume=%s No dev\n", vol->vol_name);
+      }
+      free(vol->vol_name);
+      vol->vol_name = NULL;
+   }
+   delete vol_list;
+   vol_list = NULL;
+   unlock_volumes();
+}
+
+bool DCR::can_i_use_volume()
+{
+   bool rtn = true;
+   VOLRES *vol;
+
+   lock_volumes();
+   vol = find_volume(VolumeName);
+   if (!vol) {
+      Dmsg1(dbglvl, "Vol=%s not in use.\n", VolumeName);
+      goto get_out;                   /* vol not in list */
+   }
+   ASSERT(vol->dev != NULL);
+
+   if (dev == vol->dev) {        /* same device OK */
+      Dmsg1(dbglvl, "Vol=%s on same dev.\n", VolumeName);
+      goto get_out;
+   } else {
+      Dmsg3(dbglvl, "Vol=%s on %s we have %s\n", VolumeName,
+            vol->dev->print_name(), dev->print_name());
+   }
+   /* ***FIXME*** check this ... */
+   if (!vol->dev->is_busy()) {
+      Dmsg2(dbglvl, "Vol=%s dev=%s not busy.\n", VolumeName, vol->dev->print_name());
+      goto get_out;
+   } else {
+      Dmsg2(dbglvl, "Vol=%s dev=%s busy.\n", VolumeName, vol->dev->print_name());
+   }
+   Dmsg2(dbglvl, "Vol=%s in use by %s.\n", VolumeName, vol->dev->print_name());
+   rtn = false;
+
+get_out:
+   unlock_volumes();
+   return rtn;
+
+}
index fc59596de40c2caa377735f9da79885b05b841b2..157c241f171ff34a213cf2d5468aaa5a95589588 100644 (file)
@@ -50,6 +50,12 @@ libtool on the configure command line with:
 
 
 General:
+21Oct08
+kes  Split volume management code out of src/stored/reserve.c into
+     a new file vol_mgr.c 
+kes  Modify configure to do an automatic make clean.  This ensures
+     that any changes to ./configure options are handled correctly.
+
 Beta version 2.5.16 release:
 20Oct08
 ebl  Rename JobStat table to JobHistory