]> git.sur5r.net Git - bacula/bacula/commitdiff
11Apr07
authorKern Sibbald <kern@sibbald.com>
Wed, 11 Apr 2007 20:09:25 +0000 (20:09 +0000)
committerKern Sibbald <kern@sibbald.com>
Wed, 11 Apr 2007 20:09:25 +0000 (20:09 +0000)
kes  Add exec external-command [wait-seconds] to bconsole. This
     executes the external-command.  Note! normally external-command
     should be enclosed in double quotes.
kes  Turn the .die command on only if DEVELOPER is defined -- i.e.
     it should normally be off in a production system.
10Apr07
kes  Implement die command for SD so that we can force it to dump.
kes  Implement SD lock debug code.
kes  Implement new algorithm for keeping Volume list in SD.  It
     is now owned by the device.

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

24 files changed:
bacula/src/console/console.c
bacula/src/dird/ua_dotcmds.c
bacula/src/lib/jcr.c
bacula/src/stored/acquire.c
bacula/src/stored/ansi_label.c
bacula/src/stored/askdir.c
bacula/src/stored/autochanger.c
bacula/src/stored/bacula-sd.conf.in
bacula/src/stored/bcopy.c
bacula/src/stored/block.c
bacula/src/stored/btape.c
bacula/src/stored/dev.c
bacula/src/stored/dev.h
bacula/src/stored/device.c
bacula/src/stored/dircmd.c
bacula/src/stored/label.c
bacula/src/stored/match_bsr.c
bacula/src/stored/protos.h
bacula/src/stored/reserve.c
bacula/src/stored/spool.c
bacula/src/stored/stored.h
bacula/src/stored/wait.c
bacula/src/version.h
bacula/technotes-2.1

index b01dea7b7f99782efa7384fff293b96e19be09fc..34064f774fd2dce0329fdba578cf3d4eb6755371 100644 (file)
@@ -102,6 +102,7 @@ static int quitcmd(FILE *input, BSOCK *UA_sock);
 static int echocmd(FILE *input, BSOCK *UA_sock);
 static int timecmd(FILE *input, BSOCK *UA_sock);
 static int sleepcmd(FILE *input, BSOCK *UA_sock);
+static int execcmd(FILE *input, BSOCK *UA_sock);
 
 
 #define CONFIG_FILE "bconsole.conf"   /* default configuration file */
@@ -166,6 +167,7 @@ static struct cmdstruct commands[] = {
  { N_("time"),       timecmd,      _("print current time")},
  { N_("version"),    versioncmd,   _("print Console's version")},
  { N_("echo"),       echocmd,      _("echo command string")},
+ { N_("exec"),       execcmd,      _("execute an external command")},
  { N_("exit"),       quitcmd,      _("exit = quit")},
  { N_("zed_keys"),   zed_keyscmd,  _("zed_keys = use zed keys instead of bash keys")},
              };
@@ -886,15 +888,54 @@ static int do_outputcmd(FILE *input, BSOCK *UA_sock)
    }
    fd = fopen(argk[1], mode);
    if (!fd) {
+      berrno be;
       senditf(_("Cannot open file %s for output. ERR=%s\n"),
-         argk[1], strerror(errno));
+         argk[1], be.strerror(errno));
       return 1;
    }
    output = fd;
    return 1;
 }
 
-static int echocmd(FILE *intut, BSOCK *UA_sock)
+/*
+ * exec "some-command" [wait-seconds]
+*/
+static int execcmd(FILE *input, BSOCK *UA_sock)
+{
+   BPIPE *bpipe;
+   char line[5000];
+   int stat;
+   int wait = 0;
+
+   if (argc > 3) {
+      sendit(_("Too many arguments. Enclose command in double quotes.\n"));
+      return 1;
+   }
+   if (argc == 3) {
+      wait = atoi(argk[2]);
+   }
+   bpipe = open_bpipe(argk[1], wait, "r");
+   if (!bpipe) {
+      berrno be;
+      senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
+         argk[1], be.strerror(errno));
+      return 1;
+   }
+  
+   while (fgets(line, sizeof(line), bpipe->rfd)) {
+      senditf("%s", line);
+   }
+   stat = close_bpipe(bpipe);
+   if (stat != 0) {
+      berrno be;
+      be.set_errno(stat);
+     senditf(_("Autochanger error: ERR=%s\n"), be.strerror());
+   }
+   return 1;
+}
+
+
+static int echocmd(FILE *input, BSOCK *UA_sock)
 {
    for (int i=1; i < argc; i++) {
       senditf("%s", argk[i]);
index c8479129e4371ee42a618f6c7f9a5681f20a01b3..8d41289b8afb6608b4598b6d3be842642701d0f7 100644 (file)
@@ -162,20 +162,162 @@ static bool getmsgscmd(UAContext *ua, const char *cmd)
    return 1;
 }
 
+#ifdef DEVELOPER
+static void do_storage_die(UAContext *ua, STORE *store)
+{
+   BSOCK *sd;
+   JCR *jcr = ua->jcr;
+   USTORE lstore;
+   
+   lstore.store = store;
+   pm_strcpy(lstore.store_source, _("unknown source"));
+   set_wstorage(jcr, &lstore);
+   /* Try connecting for up to 15 seconds */
+   ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
+      store->name(), store->address, store->SDport);
+   if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
+      ua->error_msg(_("Failed to connect to Storage daemon.\n"));
+      return;
+   }
+   Dmsg0(120, _("Connected to storage daemon\n"));
+   sd = jcr->store_bsock;
+   sd->fsend(".die");
+   if (sd->recv() >= 0) {
+      ua->send_msg("%s", sd->msg);
+   }
+   sd->signal(BNET_TERMINATE);
+   sd->close();
+   jcr->store_bsock = NULL;
+   return;
+}
+
+static void do_client_die(UAContext *ua, CLIENT *client)
+{
+   BSOCK *fd;
+
+   /* Connect to File daemon */
+
+   ua->jcr->client = client;
+   /* Try to connect for 15 seconds */
+   ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
+      client->name(), client->address, client->FDport);
+   if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
+      ua->error_msg(_("Failed to connect to Client.\n"));
+      return;
+   }
+   Dmsg0(120, "Connected to file daemon\n");
+   fd = ua->jcr->file_bsock;
+   fd->fsend(".die");
+   if (fd->recv() >= 0) {
+      ua->send_msg("%s", fd->msg);
+   }
+   fd->signal(BNET_TERMINATE);
+   fd->close();
+   ua->jcr->file_bsock = NULL;
+   return;
+}
+
 /*
  * Create segmentation fault
  */
 static bool diecmd(UAContext *ua, const char *cmd)
 {
+   STORE *store;
+   CLIENT *client;
+   int i;
    JCR *jcr = NULL;
    int a;
 
-   ua->send_msg(_("The Director will segment fault.\n"));
-   a = jcr->JobId; /* ref NULL pointer */
-   jcr->JobId = 1000; /* another ref NULL pointer */
+   Dmsg1(120, "diecmd:%s:\n", cmd);
+
+   /* General debug? */
+   for (i=1; i<ua->argc; i++) {
+      if (strcasecmp(ua->argk[i], "dir") == 0 ||
+          strcasecmp(ua->argk[i], "director") == 0) {
+         ua->send_msg(_("The Director will segment fault.\n"));
+         a = jcr->JobId; /* ref NULL pointer */
+         jcr->JobId = 1000; /* another ref NULL pointer */
+         return 1;
+      }
+      if (strcasecmp(ua->argk[i], "client") == 0 ||
+          strcasecmp(ua->argk[i], "fd") == 0) {
+         client = NULL;
+         if (ua->argv[i]) {
+            client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
+            if (client) {
+               do_client_die(ua, client);
+               return 1;
+            }
+         }
+         client = select_client_resource(ua);
+         if (client) {
+            do_client_die(ua, client);
+            return 1;
+         }
+      }
+
+      if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
+          strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
+          strcasecmp(ua->argk[i], NT_("sd")) == 0) {
+         store = NULL;
+         if (ua->argv[i]) {
+            store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
+            if (store) {
+               do_storage_die(ua, store);
+               return 1;
+            }
+         }
+         store = get_storage_resource(ua, false/*no default*/);
+         if (store) {
+            do_storage_die(ua, store);
+            return 1;
+         }
+      }
+   }
+   /*
+    * We didn't find an appropriate keyword above, so
+    * prompt the user.
+    */
+   start_prompt(ua, _("Available daemons are: \n"));
+   add_prompt(ua, _("Director"));
+   add_prompt(ua, _("Storage"));
+   add_prompt(ua, _("Client"));
+   switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) {
+   case 0:                         /* Director */
+      ua->send_msg(_("The Director will segment fault.\n"));
+      a = jcr->JobId; /* ref NULL pointer */
+      jcr->JobId = 1000; /* another ref NULL pointer */
+      break;
+   case 1:
+      store = get_storage_resource(ua, false/*no default*/);
+      if (store) {
+         do_storage_die(ua, store);
+      }
+      break;
+   case 2:
+      client = select_client_resource(ua);
+      if (client) {
+         do_client_die(ua, client);
+      }
+      break;
+   default:
+      break;
+   }
+   return true;
+}
+
+#else
+
+/*
+ * Dummy routine for non-development version
+ */
+static bool diecmd(UAContext *ua, const char *cmd)
+{
    return true;
 }
 
+#endif
+
 static bool jobscmd(UAContext *ua, const char *cmd)
 {
    JOB *job;
index 8f65f71f7f403c6cf4428087f75486a6d76b10ac..982f4d0a9b37f3cf3434781ffaf7fdf61fdcf27d 100644 (file)
@@ -1,31 +1,7 @@
-/*
- * Manipulation routines for Job Control Records and
- *  handling of last_jobs_list.
- *
- *  Kern E. Sibbald, December 2000
- *
- *  Version $Id$
- *
- *  These routines are thread safe.
- *
- *  The job list routines were re-written in May 2005 to
- *  eliminate the global lock while traversing the list, and
- *  to use the dlist subroutines.  The locking is now done
- *  on the list each time the list is modified or traversed.
- *  That is it is "micro-locked" rather than globally locked.
- *  The result is that there is one lock/unlock for each entry
- *  in the list while traversing it rather than a single lock
- *  at the beginning of a traversal and one at the end.  This
- *  incurs slightly more overhead, but effectively eliminates 
- *  the possibilty of race conditions.  In addition, with the
- *  exception of the global locking of the list during the
- *  re-reading of the config file, no recursion is needed.
- *
- */
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
+   Copyright (C) 2000-2007 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.
    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
    Switzerland, email:ftf@fsfeurope.org.
 */
+/*
+ * Manipulation routines for Job Control Records and
+ *  handling of last_jobs_list.
+ *
+ *  Kern E. Sibbald, December 2000
+ *
+ *  Version $Id$
+ *
+ *  These routines are thread safe.
+ *
+ *  The job list routines were re-written in May 2005 to
+ *  eliminate the global lock while traversing the list, and
+ *  to use the dlist subroutines.  The locking is now done
+ *  on the list each time the list is modified or traversed.
+ *  That is it is "micro-locked" rather than globally locked.
+ *  The result is that there is one lock/unlock for each entry
+ *  in the list while traversing it rather than a single lock
+ *  at the beginning of a traversal and one at the end.  This
+ *  incurs slightly more overhead, but effectively eliminates 
+ *  the possibilty of race conditions.  In addition, with the
+ *  exception of the global locking of the list during the
+ *  re-reading of the config file, no recursion is needed.
+ *
+ */
 
 #include "bacula.h"
 #include "jcr.h"
index 436c39d2357b57594eecfb8f32028929b28bffc8..8eac9dbc87552e0d9b8ee1514ef46c7e3996e484 100644 (file)
@@ -130,7 +130,7 @@ bool acquire_device_for_read(DCR *dcr)
       unlock_reservations();
       if (stat == 1) {
          DCR *new_dcr = jcr->read_dcr;
-         dev->unblock();
+         dev->unblock(dev_unlocked);
          detach_dcr_from_dev(dcr);    /* release old device */
          /* Copy important info from the new dcr */
          dev = dcr->dev = new_dcr->dev; 
@@ -281,14 +281,13 @@ default_path:
       dcr->VolumeName, dev->print_name());
 
 get_out:
-   dev->lock();
+   dev->dlock();
    if (dcr->reserved_device) {
       dev->reserved_device--;
       Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name());
       dcr->reserved_device = false;
    }
-   dev->unlock();
-   dev->unblock();
+   dev->unblock(dev_locked);
    Dmsg1(50, "jcr->dcr=%p\n", jcr->dcr);
    return ok;
 }
@@ -348,7 +347,7 @@ DCR *acquire_device_for_append(DCR *dcr)
             dcr->VolumeName);
          /* Release volume reserved by dir_find_next_appendable_volume() */
          if (dcr->VolumeName[0]) {
-            free_unused_volume(dcr);
+            volume_unused(dcr);
          }
          if (dev->num_writers != 0) {
             Jmsg3(jcr, M_FATAL, 0, _("Wanted to append to Volume \"%s\", but device %s is busy writing on \"%s\" .\n"), 
@@ -431,28 +430,26 @@ DCR *acquire_device_for_append(DCR *dcr)
    }
    dev->VolCatInfo.VolCatJobs++;              /* increment number of jobs on vol */
    dir_update_volume_info(dcr, false);        /* send Volume info to Director */
-   dev->lock();
+   dev->dlock();
    if (dcr->reserved_device) {
       dev->reserved_device--;
       Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name());
       dcr->reserved_device = false;
    }
-   dev->unlock();
-   dev->unblock();
+   dev->unblock(dev_locked);
    return dcr;
 
 /*
  * Error return
  */
 get_out:
-   dev->lock();
+   dev->dlock();
    if (dcr->reserved_device) {
       dev->reserved_device--;
       Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name());
       dcr->reserved_device = false;
    }
-   dev->unlock();
-   dev->unblock();
+   dev->unblock(dev_locked);
    return NULL;
 }
 
@@ -473,7 +470,7 @@ bool release_device(DCR *dcr)
 
    /* lock only if not already locked by this thread */
    if (!dcr->dev_locked) {
-      lock_device(dev);
+      dev->r_dlock();
    }
    Dmsg2(100, "release_device device %s is %s\n", dev->print_name(), dev->is_tape()?"tape":"disk");
 
@@ -558,7 +555,7 @@ bool release_device(DCR *dcr)
       free_pool_memory(alert);
    }
    dcr->dev_locked = false;              /* set no longer locked */
-   dev->unlock();
+   dev->dunlock();
    if (jcr->read_dcr == dcr) {
       jcr->read_dcr = NULL;
    }
@@ -579,6 +576,7 @@ DCR *new_dcr(JCR *jcr, DEVICE *dev)
    memset(dcr, 0, sizeof(DCR));
    dcr->jcr = jcr;
    if (dev) {
+      dcr->tid = pthread_self();
       dcr->dev = dev;
       dcr->device = dev->device;
       dcr->block = new_block(dev);
@@ -629,24 +627,7 @@ static void attach_dcr_to_dev(DCR *dcr)
 
 void detach_dcr_from_dev(DCR *dcr)
 {
-   DEVICE *dev = dcr->dev;
-
-   if (dcr->reserved_device) {
-      dcr->reserved_device = false;
-      lock_device(dev);
-      dev->reserved_device--;
-      Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name());
-      dcr->reserved_device = false;
-      /* If we set read mode in reserving, remove it */
-      if (dev->can_read()) {
-         dev->clear_read();
-      }
-      if (dev->num_writers < 0) {
-         Jmsg1(dcr->jcr, M_ERROR, 0, _("Hey! num_writers=%d!!!!\n"), dev->num_writers);
-         dev->num_writers = 0;
-      }
-      dev->unlock();
-   }
+   unreserve_device(dcr);
 
    /* Detach this dcr only if attached */
    if (dcr->attached_to_dev) {
@@ -654,7 +635,6 @@ void detach_dcr_from_dev(DCR *dcr)
       dcr->attached_to_dev = false;
 //    remove_dcr_from_dcrs(dcr);      /* remove dcr from jcr list */
    }
-   free_unused_volume(dcr);           /* free unused vols attached to this dcr */
    pthread_cond_broadcast(&dcr->dev->wait_next_vol);
    pthread_cond_broadcast(&wait_device_release);
 }
index 3e1489278bbb24baad8255821e96755dde34a98a..76005940a0fb42aebd98fbb27fdc0b63d12be3f8 100644 (file)
@@ -145,7 +145,7 @@ int read_ansi_ibm_label(DCR *dcr)
                   *q++ = *p++;
                }
                *q = 0;
-               new_volume(dcr, dev->VolHdr.VolumeName);
+               reserve_volume(dcr, dev->VolHdr.VolumeName);
                Dmsg2(100, "Wanted ANSI Vol %s got %6s\n", VolName, dev->VolHdr.VolumeName);
                Mmsg2(jcr->errmsg, _("Wanted ANSI Volume \"%s\" got \"%s\"\n"), VolName, dev->VolHdr.VolumeName);
                return VOL_NAME_ERROR;
index dbc2b8fe2e0edd0a0c0ad0532fe7553888dede3e..3f728742b4eb03956cb330c510273ec20cfe7d7d 100644 (file)
@@ -1,11 +1,3 @@
-/*
- *  Subroutines to handle Catalog reqests sent to the Director
- *   Reqests/commands from the Director are handled in dircmd.c
- *
- *   Kern Sibbald, December 2000
- *
- *   Version $Id$
- */
 /*
    Bacula® - The Network Backup Solution
 
    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
    Switzerland, email:ftf@fsfeurope.org.
 */
+/*
+ *  Subroutines to handle Catalog reqests sent to the Director
+ *   Reqests/commands from the Director are handled in dircmd.c
+ *
+ *   Kern Sibbald, December 2000
+ *
+ *   Version $Id$
+ */
 
 #include "bacula.h"                   /* pull in global headers */
 #include "stored.h"                   /* pull in Storage Deamon headers */
@@ -237,8 +237,10 @@ bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
 
 /*
  * Get info on the next appendable volume in the Director's database
- * Returns: true  on success
- *          false on failure
+ *
+ * Returns: true  on success dcr->VolumeName is volume
+ *                reserve_volume() called on Volume name
+ *          false on failure dcr->VolumeName[0] == 0
  *
  *          Volume information returned in dcr
  *
@@ -282,7 +284,7 @@ bool dir_find_next_appendable_volume(DCR *dcr)
     }
     if (found) {
        Dmsg0(400, "dir_find_next_appendable_volume return true\n");
-       new_volume(dcr, dcr->VolumeName);   /* reserve volume */
+       reserve_volume(dcr, dcr->VolumeName);   /* reserve volume */
        V(vol_info_mutex);
        unlock_reservations();
        return true;
@@ -468,9 +470,9 @@ bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
          Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
          return false;
       }
-      dev->lock();  
+      dev->dlock();  
       got_vol = dir_find_next_appendable_volume(dcr);   /* get suggested volume */
-      dev->unlock();
+      dev->dunlock();
       if (got_vol) {
          return true;
       } else {
index 4c06a83315f1562ffa793076bcabe10b51558e21..be656ff3371d73fb1b226c3e6c56f8db4a5b8ada 100644 (file)
@@ -264,7 +264,7 @@ int get_autochanger_loaded_slot(DCR *dcr)
    *results.c_str() = 0;
    Dmsg1(100, "Run program=%s\n", changer);
    status = run_program_full_output(changer, timeout, results.c_str());
-   Dmsg3(100, "run_prog: %s stat=%d result=%s\n", changer, status, results.c_str());
+   Dmsg3(100, "run_prog: %s stat=%d result=%s", changer, status, results.c_str());
    if (status == 0) {
       loaded = str_to_int32(results.c_str());
       if (loaded > 0) {
@@ -414,14 +414,14 @@ static bool unload_other_drive(DCR *dcr, int slot)
       }
       break;
    }
-   dev->lock();  
+   dev->dlock();
    if (dev->is_busy()) {
       Jmsg(jcr, M_WARNING, 0, _("Volume \"%s\" is in use by device %s\n"),
            dcr->VolumeName, dev->print_name());
       Dmsg4(100, "Vol %s for dev=%s is busy dev=%s slot=%d\n",
            dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), slot);
       Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->reserved_device);
-      dev->unlock();
+      dev->dunlock();
       return false;
    }
 
@@ -463,7 +463,7 @@ static bool unload_other_drive(DCR *dcr, int slot)
       Dmsg0(100, "Slot unloaded\n");
    }
    unlock_changer(dcr);
-   dev->unlock();
+   dev->dunlock();
    free_pool_memory(changer_cmd);
    return ok;
 }
index 367951ecb4d716ffddc68765c74fa8c7af78b38d..3ca34340be1534f51d13fc4f6ba8db4d8f9f3e2a 100644 (file)
@@ -160,7 +160,7 @@ Device {
 # A DVD device
 #
 #Device {
-#  Name = "DVD-Writer"
+#  Name = "DVD Writer"
 #  Media Type = DVD
 #  Device Type = DVD
 #  Archive Device = /dev/hdc
index b8a6bd5599d5a2cc7e4acc5ca611939d3186c14f..2412d84d6e0ad8fd6baa35000ace1d78be920f68 100644 (file)
@@ -1,16 +1,7 @@
-/*
- *
- *  Program to copy a Bacula from one volume to another.
- *
- *   Kern E. Sibbald, October 2002
- *
- *
- *   Version $Id$
- */
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2002-2006 Free Software Foundation Europe e.V.
+   Copyright (C) 2002-2007 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.
    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
    Switzerland, email:ftf@fsfeurope.org.
 */
+/*
+ *
+ *  Program to copy a Bacula from one volume to another.
+ *
+ *   Kern E. Sibbald, October 2002
+ *
+ *
+ *   Version $Id$
+ */
 
 #include "bacula.h"
 #include "stored.h"
@@ -186,13 +186,13 @@ int main (int argc, char *argv[])
    }
    Dmsg0(100, "About to acquire device for writing\n");
    /* For we must now acquire the device for writing */
-   lock_device(out_dev);
+   out_dev->r_dlock();
    if (out_dev->open(out_jcr->dcr, OPEN_READ_WRITE) < 0) {
       Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), out_dev->errmsg);
-      out_dev->unlock();
+      out_dev->dunlock();
       exit(1);
    }
-   out_dev->unlock();
+   out_dev->dunlock();
    if (!acquire_device_for_append(out_jcr->dcr)) {
       free_jcr(in_jcr);
       exit(1);
index faa51c94d586b6bd542a72d0a527caf914e20bb8..a2dd5a01cb1dcc5e94544288d468e7da343180fd 100644 (file)
@@ -346,7 +346,7 @@ bool write_block_to_device(DCR *dcr)
    }
 
    if (!dcr->dev_locked) {            /* device already locked? */
-      lock_device(dev);               /* no, lock it */
+      dev->r_dlock();                  /* no, lock it */
    }
 
    /*
@@ -388,7 +388,7 @@ bool write_block_to_device(DCR *dcr)
 
 bail_out:
    if (!dcr->dev_locked) {            /* did we lock dev above? */
-      dev->unlock();                  /* unlock it now */
+      dev->dunlock();                  /* unlock it now */
    }
    return stat;
 }
@@ -896,9 +896,9 @@ bool read_block_from_device(DCR *dcr, bool check_block_numbers)
    bool ok;
    DEVICE *dev = dcr->dev;
    Dmsg0(200, "Enter read_block_from_device\n");
-   lock_device(dev);
+   dev->r_dlock();
    ok = read_block_from_dev(dcr, check_block_numbers);
-   unlock_device(dev);
+   dev->dunlock();
    Dmsg0(200, "Leave read_block_from_device\n");
    return ok;
 }
index 76e73315b7b9772ae8e0888e9e7d7719d58b2f3e..4f4560475dde3aaf8829076c7e702e3224d26464 100644 (file)
@@ -358,7 +358,7 @@ static bool open_the_device()
    bool ok = true;
 
    block = new_block(dev);
-   lock_device(dev);
+   dev->r_dlock();
    Dmsg1(200, "Opening device %s\n", dcr->VolumeName);
    if (dev->open(dcr, OPEN_READ_WRITE) < 0) {
       Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), dev->errmsg);
@@ -369,7 +369,7 @@ static bool open_the_device()
    dev->set_append();                 /* put volume in append mode */
 
 bail_out:
-   dev->unlock();
+   dev->dunlock();
    free_block(block);
    return ok;
 }
@@ -2330,7 +2330,7 @@ static int flush_block(DEV_BLOCK *block, int dump)
    DEV_BLOCK *tblock;
    uint32_t this_file, this_block_num;
 
-   lock_device(dev);
+   dev->r_dlock();
    if (!this_block) {
       this_block = new_block(dev);
    }
@@ -2385,12 +2385,12 @@ static int flush_block(DEV_BLOCK *block, int dump)
          if (!fixup_device_block_write_error(jcr->dcr)) {
             Pmsg1(000, _("Cannot fixup device error. %s\n"), dev->bstrerror());
             ok = false;
-            dev->unlock();
+            dev->dunlock();
             return 0;
          }
          BlockNumber = 0;             /* start counting for second tape */
       }
-      dev->unlock();
+      dev->dunlock();
       return 1;                       /* end of tape reached */
    }
 
@@ -2409,7 +2409,7 @@ static int flush_block(DEV_BLOCK *block, int dump)
    last_file = this_file;
    last_block_num = this_block_num;
 
-   dev->unlock();
+   dev->dunlock();
    return 1;
 }
 
index f63f70f79afadcaaf2576cee08e6c20117847c13..802b0647a91e7fe98726721b9bf181da215cfbdb 100644 (file)
@@ -1,3 +1,30 @@
+/*
+   Bacula® - The Network Backup Solution
+
+   Copyright (C) 2000-2007 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 plus additions
+   that are listed 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 John Walker.
+   The licensor of Bacula is the Free Software Foundation Europe
+   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+   Switzerland, email:ftf@fsfeurope.org.
+*/
 /*
  *
  *   dev.c  -- low level operations on device (storage device)
@@ -5,7 +32,7 @@
  *              Kern Sibbald, MM
  *
  *     NOTE!!!! None of these routines are reentrant. You must
- *        use lock_device() and dev->unlock() at a higher level,
+ *        use dev->r_dlock() and dev->unlock() at a higher level,
  *        or use the xxx_device() equivalents.  By moving the
  *        thread synchronization to a higher level, we permit
  *        the higher level routines to "seize" the device and
  *
  *   Version $Id$
  */
-/*
-   Bacula® - The Network Backup Solution
-
-   Copyright (C) 2000-2007 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 plus additions
-   that are listed 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 John Walker.
-   The licensor of Bacula is the Free Software Foundation Europe
-   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
-   Switzerland, email:ftf@fsfeurope.org.
-*/
 
 /*
  * Handling I/O errors and end of tape conditions are a bit tricky.
@@ -782,18 +782,21 @@ bool DEVICE::rewind(DCR *dcr)
 
 void DEVICE::block(int why)
 {
-   lock_device(this);
+   r_dlock();              /* need recursive lock to block */
    block_device(this, why);
-   unlock();
+   r_dunlock();
 }
 
-void DEVICE::unblock()
-{  
-   lock();   
+void DEVICE::unblock(bool locked)
+{
+   if (!locked) {
+      dlock();
+   }
    unblock_device(this);
-   unlock();
+   dunlock();
 }
 
+
 const char *DEVICE::print_blocked() const 
 {
    switch (m_blocked) {
@@ -1850,6 +1853,7 @@ void DEVICE::clrerror(int func)
 void DEVICE::clear_volhdr()
 {
    free_volume(this);
+   Dmsg1(100, "Clear volhdr vol=%s\n", VolHdr.VolumeName);
    memset(&VolHdr, 0, sizeof(VolHdr));
 }
 
index b37c48d1955da500c8cffb57adfa1cedaaa08d1e..0fa2847e7a280fb2e40e5095f04a006c278b594a 100644 (file)
 #ifndef __DEV_H
 #define __DEV_H 1
 
+#ifdef SD_DEBUG_LOCK
+#define r_dlock() _r_dlock(__FILE__, __LINE__);      /* in device.c */
+#define r_dunlock() _r_dunlock(__FILE__, __LINE__);  /* in device.c */
+#define dlock() _dlock(__FILE__, __LINE__);          /* in device.c */
+#define dunlock() _dunlock(__FILE__, __LINE__);     /* in device.c */
+#endif
+
 #undef DCR                            /* used by Bacula */
 
-#define lock_device(d) _lock_device(__FILE__, __LINE__, (d))
-#define unlock_device(d) _unlock_device(__FILE__, __LINE__, (d))
 #define block_device(d, s) _block_device(__FILE__, __LINE__, (d), s)
 #define unblock_device(d) _unblock_device(__FILE__, __LINE__, (d))
 #define steal_device_lock(d, p, s) _steal_device_lock(__FILE__, __LINE__, (d), (p), s)
@@ -190,6 +195,16 @@ typedef struct s_steal_lock {
 class DEVRES;                        /* Device resource defined in stored_conf.h */
 
 class DCR; /* forward reference */
+class VOLRES; /* forward reference */
+
+/*
+ * Used in unblock() call
+ */
+enum {
+   dev_locked = true,
+   dev_unlocked = false
+};
+
 /*
  * Device structure definition. There is one of these for
  *  each physical device. Everything here is "global" to
@@ -199,6 +214,8 @@ class DEVICE {
 private:
    int m_fd;                          /* file descriptor */
    int m_blocked;                     /* set if we must wait (i.e. change tape) */
+   int m_count;                       /* Mutex use count -- DEBUG only */
+   pthread_t m_pid;                   /* Thread that locked -- DEBUG only */
 public:
    dlist *attached_dcrs;              /* attached DCR list */
    pthread_mutex_t m_mutex;           /* access control */
@@ -258,6 +275,7 @@ public:
    
    utime_t  vol_poll_interval;        /* interval between polling Vol mount */
    DEVRES *device;                    /* pointer to Device Resource */
+   VOLRES *vol;                       /* Pointer to Volume reservation item */
    btimer_t *tid;                     /* timer id */
 
    VOLUME_CAT_INFO VolCatInfo;        /* Volume Catalog Information */
@@ -360,12 +378,22 @@ public:
    void clear_freespace_ok() { state &= ~ST_FREESPACE_OK; };
    char *bstrerror(void) { return errmsg; };
    char *print_errmsg() { return errmsg; };
-   void lock() { P(m_mutex); } 
-   void unlock() { V(m_mutex); } 
+
+#ifdef  SD_DEBUG_LOCK
+   void _r_dlock(const char *, int);      /* in device.c */
+   void _r_dunlock(const char *, int);    /* in device.c */
+   void _dlock(const char *, int);        /* in device.c */
+   void _dunlock(const char *, int);      /* in device.c */
+#else
+   void r_dlock();                        /* in device.c */
+   void r_dunlock();                      /* in device.c */
+   void dlock() { P(m_mutex); } 
+   void dunlock() { V(m_mutex); } 
+#endif
 
    void clear_volhdr();          /* in dev.c */
    void block(int why);          /* in dev.c */
-   void unblock();               /* in dev.c */
+   void unblock(bool locked=false); /* in dev.c */
    void close();                 /* in dev.c */
    void close_part(DCR *dcr);    /* in dev.c */
    bool truncate(DCR *dcr);      /* in dev.c */
@@ -411,7 +439,6 @@ private:
    void open_dvd_device(DCR *dcr, int omode);  /* in dev.c */
 };
 
-/* Note, these return int not bool! */
 inline const char *DEVICE::strerror() const { return errmsg; }
 inline const char *DEVICE::archive_name() const { return dev_name; }
 inline const char *DEVICE::print_name() const { return prt_name; }
@@ -431,6 +458,7 @@ public:
    DEVRES *device;                    /* pointer to device resource */
    DEV_BLOCK *block;                  /* pointer to block */
    DEV_RECORD *rec;                   /* pointer to record */
+   pthread_t tid;                     /* Thread running this dcr */
    int spool_fd;                      /* fd if spooling */
    bool spool_data;                   /* set to spool data */
    bool spooling;                     /* set when actually spooling */
@@ -470,7 +498,6 @@ public:
    dlink link;
    char *vol_name;
    DEVICE *dev;
-   DCR *dcr;
 };
 
 
index 2696ecc0b506f04535fe0785a8f2da0238e9e65d..ba90a76c37f563da7c6028ba209cc1308c0e9d3d 100644 (file)
@@ -39,7 +39,7 @@
  *        Obviously, no zzz_dev() is allowed to call
  *        a www_device() or everything falls apart.
  *
- * Concerning the routines lock_device() and block_device()
+ * Concerning the routines dev->r_lock()() and block_device()
  *  see the end of this module for details.  In general,
  *  blocking a device leaves it in a state where all threads
  *  other than the current thread block when they attempt to
 #include "bacula.h"                   /* pull in global headers */
 #include "stored.h"                   /* pull in Storage Deamon headers */
 
+#ifdef SD_DEBUG_LOCK
+const int dbglvl = 0;
+#else
+const int dbglvl = 500;
+#endif
+
+
 /* Forward referenced functions */
 
 /*
@@ -99,7 +106,7 @@ bool fixup_device_block_write_error(DCR *dcr)
 
    block_device(dev, BST_DOING_ACQUIRE);
    /* Unlock, but leave BLOCKED */
-   dev->unlock();
+   dev->dunlock();
 
    bstrncpy(PrevVolName, dev->VolCatInfo.VolCatName, sizeof(PrevVolName));
    bstrncpy(dev->VolHdr.PrevVolumeName, PrevVolName, sizeof(dev->VolHdr.PrevVolumeName));
@@ -116,11 +123,11 @@ bool fixup_device_block_write_error(DCR *dcr)
    if (!mount_next_write_volume(dcr, 1)) {
       free_block(label_blk);
       dcr->block = block;
-      dev->lock();  
+      dev->dlock();  
       unblock_device(dev);
       return false;                /* device locked */
    }
-   dev->lock();                    /* lock again */
+   dev->dlock();                    /* lock again */
 
    dev->VolCatInfo.VolCatJobs++;              /* increment number of jobs on vol */
    dir_update_volume_info(dcr, false);        /* send Volume info to Director */
@@ -261,7 +268,7 @@ bool first_open_device(DCR *dcr)
       return false;
    }
 
-   lock_device(dev);
+   dev->r_dlock();
 
    /* Defer opening files */
    if (!dev->is_tape()) {
@@ -284,7 +291,7 @@ bool first_open_device(DCR *dcr)
    Dmsg1(129, "open dev %s OK\n", dev->print_name());
 
 bail_out:
-   dev->unlock();
+   dev->dunlock();
    return ok;
 }
 
@@ -316,28 +323,21 @@ bool open_device(DCR *dcr)
    return true;
 }
 
-
 /*
- * When dev_blocked is set, all threads EXCEPT thread with id no_wait_id
- * must wait. The no_wait_id thread is out obtaining a new volume
- * and preparing the label.
+ * Find which JobId corresponds to the current thread
  */
-void _lock_device(const char *file, int line, DEVICE *dev)
+uint32_t get_jobid_from_tid()
 {
-   int stat;
-   Dmsg3(500, "lock %d from %s:%d\n", dev->blocked(), file, line);
-   dev->lock();   
-   if (dev->blocked() && !pthread_equal(dev->no_wait_id, pthread_self())) {
-      dev->num_waiting++;             /* indicate that I am waiting */
-      while (dev->blocked()) {
-         if ((stat = pthread_cond_wait(&dev->wait, &dev->m_mutex)) != 0) {
-            dev->unlock();
-            Emsg1(M_ABORT, 0, _("pthread_cond_wait failure. ERR=%s\n"),
-               strerror(stat));
-         }
+   JCR *jcr;
+   uint32_t JobId = 0;
+   foreach_jcr(jcr) {
+      if (pthread_equal(jcr->my_thread_id, pthread_self())) {
+         JobId = (uint32_t)jcr->JobId;
+         break;
       }
-      dev->num_waiting--;             /* no longer waiting */
    }
+   endeach_jcr(jcr);
+   return JobId;
 }
 
 /*
@@ -352,23 +352,91 @@ bool is_device_unmounted(DEVICE *dev)
    return stat;
 }
 
-void _unlock_device(const char *file, int line, DEVICE *dev)
+void DEVICE::_dlock(const char *file, int line)
 {
-   Dmsg2(500, "unlock from %s:%d\n", file, line);
-   dev->unlock();
+   Dmsg4(sd_dbglvl, "dlock from %s:%d precnt=%d JobId=%u\n", file, line,
+         m_count, get_jobid_from_tid()); 
+   /* Note, this *really* should be protected by a mutex, but
+    *  since it is only debug code we don't worry too much.  
+    */
+   if (m_count > 0 && pthread_equal(m_pid, pthread_self())) {
+      Dmsg2(sd_dbglvl, "DEADLOCK !!!!!!!!!! from %s:%d\n", file, line);
+   }
+   P(m_mutex);
+   m_pid = pthread_self();
+   m_count++; 
+}
+
+void DEVICE::_dunlock(const char *file, int line)
+{
+   m_count--; 
+   Dmsg4(sd_dbglvl, "dunlock from %s:%d postcnt=%d JobId=%u\n", file, line,
+         m_count, get_jobid_from_tid()); 
+   V(m_mutex);   
+}
+
+#ifdef SD_DEBUG_LOCK
+void DEVICE::_r_dunlock(const char *file, int line)
+{
+   this->_dunlock(file, line);
+}
+#else
+void DEVICE::r_dunlock()
+{
+   this->dunlock();
+}
+#endif
+
+
+/*
+ * This is a recursive lock that checks if the device is blocked.
+ *
+ * When blocked is set, all threads EXCEPT thread with id no_wait_id
+ * must wait. The no_wait_id thread is out obtaining a new volume
+ * and preparing the label.
+ */
+#ifdef SD_DEBUG_LOCK
+void DEVICE::_r_dlock(const char *file, int line)
+#else
+void DEVICE::r_dlock()
+#endif
+{
+   int stat;
+#ifdef SD_DEBUG_LOCK
+   Dmsg4(dbglvl, "r_dlock blked=%s from %s:%d JobId=%u\n", this->print_blocked(),
+         file, line, get_jobid_from_tid());
+#else
+   Dmsg1dbglvl, "reclock blked=%s\n", this->print_blocked());
+#endif
+   this->dlock();   
+   if (this->blocked() && !pthread_equal(this->no_wait_id, pthread_self())) {
+      this->num_waiting++;             /* indicate that I am waiting */
+      while (this->blocked()) {
+         Dmsg3(dbglvl, "r_dlock blked=%s no_wait=%p me=%p\n", this->print_blocked(),
+               this->no_wait_id, pthread_self());
+         if ((stat = pthread_cond_wait(&this->wait, &m_mutex)) != 0) {
+            berrno be;
+            this->dunlock();
+            Emsg1(M_ABORT, 0, _("pthread_cond_wait failure. ERR=%s\n"),
+               be.strerror(stat));
+         }
+      }
+      this->num_waiting--;             /* no longer waiting */
+   }
 }
 
 /*
  * Block all other threads from using the device
  *  Device must already be locked.  After this call,
- *  the device is blocked to any thread calling lock_device(),
+ *  the device is blocked to any thread calling dev->r_lock(),
  *  but the device is not locked (i.e. no P on device).  Also,
- *  the current thread can do slip through the lock_device()
+ *  the current thread can do slip through the dev->r_lock()
  *  calls without blocking.
  */
 void _block_device(const char *file, int line, DEVICE *dev, int state)
 {
-   Dmsg3(500, "block set %d from %s:%d\n", state, file, line);
+   Dmsg3(dbglvl, "block set %d from %s:%d\n", state, file, line);
+
    ASSERT(dev->blocked() == BST_NOT_BLOCKED);
    dev->set_blocked(state);           /* make other threads wait */
    dev->no_wait_id = pthread_self();  /* allow us to continue */
@@ -376,10 +444,12 @@ void _block_device(const char *file, int line, DEVICE *dev, int state)
 
 /*
  * Unblock the device, and wake up anyone who went to sleep.
+ * Enter: device locked
+ * Exit:  device locked
  */
 void _unblock_device(const char *file, int line, DEVICE *dev)
 {
-   Dmsg3(500, "unblock %s from %s:%d\n", dev->print_blocked(), file, line);
+   Dmsg3(dbglvl, "unblock %s from %s:%d\n", dev->print_blocked(), file, line);
    ASSERT(dev->blocked());
    dev->set_blocked(BST_NOT_BLOCKED);
    dev->no_wait_id = 0;
@@ -395,15 +465,15 @@ void _unblock_device(const char *file, int line, DEVICE *dev)
 void _steal_device_lock(const char *file, int line, DEVICE *dev, bsteal_lock_t *hold, int state)
 {
 
-   Dmsg3(400, "steal lock. old=%s from %s:%d\n", dev->print_blocked(),
+   Dmsg3(dbglvl, "steal lock. old=%s from %s:%d\n", dev->print_blocked(),
       file, line);
    hold->dev_blocked = dev->blocked();
    hold->dev_prev_blocked = dev->dev_prev_blocked;
    hold->no_wait_id = dev->no_wait_id;
    dev->set_blocked(state);
-   Dmsg1(400, "steal lock. new=%s\n", dev->print_blocked());
+   Dmsg1(dbglvl, "steal lock. new=%s\n", dev->print_blocked());
    dev->no_wait_id = pthread_self();
-   dev->unlock();
+   dev->dunlock();
 }
 
 /*
@@ -412,13 +482,13 @@ void _steal_device_lock(const char *file, int line, DEVICE *dev, bsteal_lock_t *
  */
 void _give_back_device_lock(const char *file, int line, DEVICE *dev, bsteal_lock_t *hold)
 {
-   Dmsg3(400, "return lock. old=%s from %s:%d\n",
+   Dmsg3(dbglvl, "return lock. old=%s from %s:%d\n",
       dev->print_blocked(), file, line);
-   dev->lock();
+   dev->dlock();
    dev->set_blocked(hold->dev_blocked);
    dev->dev_prev_blocked = hold->dev_prev_blocked;
    dev->no_wait_id = hold->no_wait_id;
-   Dmsg1(400, "return lock. new=%s\n", dev->print_blocked());
+   Dmsg1(dbglvl, "return lock. new=%s\n", dev->print_blocked());
    if (dev->num_waiting > 0) {
       pthread_cond_broadcast(&dev->wait); /* wake them up */
    }
index ba5a2140c6ce597e73bcbf542927e1ce7fe3c3e5..77ff63e0587ee8166e08ee151e29d12941013ffa 100644 (file)
@@ -34,7 +34,7 @@
  *    in job.c.
  *
  *    N.B. in this file, in general we must use P(dev->mutex) rather
- *      than lock_device(dev) so that we can examine the blocked
+ *      than dev->r_lock() so that we can examine the blocked
  *      state rather than blocking ourselves because a Job
  *      thread has the device blocked. In some "safe" cases,
  *      we can do things to a blocked device. CAREFUL!!!!
@@ -75,6 +75,7 @@ extern bool qstatus_cmd(JCR *jcr);
 
 /* Forward referenced functions */
 static bool label_cmd(JCR *jcr);
+static bool die_cmd(JCR *jcr);
 static bool relabel_cmd(JCR *jcr);
 static bool readlabel_cmd(JCR *jcr);
 static bool release_cmd(JCR *jcr);
@@ -107,6 +108,7 @@ static struct s_cmds cmds[] = {
    {"autochanger", changer_cmd,     0},
    {"bootstrap",   bootstrap_cmd,   0},
    {"cancel",      cancel_cmd,      0},
+   {".die",        die_cmd,         0},
    {"label",       label_cmd,       0},     /* label a tape */
    {"mount",       mount_cmd,       0},
    {"readlabel",   readlabel_cmd,   0},
@@ -168,6 +170,7 @@ void *handle_connection_request(void *arg)
     */
    Dmsg1(110, "Conn: %s", bs->msg);
    if (sscanf(bs->msg, "Hello Start Job %127s", name) == 1) {
+      Dmsg0(110, "Got a FD connection\n");
       handle_filed_connection(bs, name);
       return NULL;
    }
@@ -175,7 +178,7 @@ void *handle_connection_request(void *arg)
    /* 
     * This is a connection from the Director, so setup a JCR 
     */
-   Dmsg0(110, "Start Dir Job\n");
+   Dmsg0(110, "Got a DIR connection\n");
    jcr = new_jcr(sizeof(JCR), stored_free_jcr); /* create Job Control Record */
    jcr->dir_bsock = bs;               /* save Director bsock */
    jcr->dir_bsock->set_jcr(jcr);
@@ -239,6 +242,24 @@ bail_out:
    return NULL;
 }
 
+
+/*
+ * Force SD to die, and hopefully dump itself.  Turned on only
+ *  in development version.
+ */
+static bool die_cmd(JCR *jcr)
+{
+#ifdef DEVELOPER
+   JCR *djcr = NULL;
+   int a;
+   Pmsg0(000, "I have been requested to die ...");
+   a = djcr->JobId;   /* ref NULL pointer */
+#endif
+   return 0;
+}
+
+     
+
 /*
  * Set debug level as requested by the Director
  *
@@ -355,7 +376,7 @@ static bool do_label(JCR *jcr, int relabel)
       dcr = find_device(jcr, dev_name, drive);
       if (dcr) {
          dev = dcr->dev;
-         dev->lock();                 /* Use P to avoid indefinite block */
+         dev->dlock();                 /* Use P to avoid indefinite block */
          if (!dev->is_open()) {
             Dmsg1(400, "Can %slabel. Device is not open\n", relabel?"re":"");
             label_volume_if_ok(dcr, oldname, newname, poolname, slot, relabel);
@@ -370,7 +391,7 @@ static bool do_label(JCR *jcr, int relabel)
             Dmsg0(400, "Can relabel. device not used\n");
             label_volume_if_ok(dcr, oldname, newname, poolname, slot, relabel);
          }
-         dev->unlock();
+         dev->dunlock();
          free_dcr(dcr);
          jcr->dcr = NULL;
       } else {
@@ -619,7 +640,7 @@ static bool mount_cmd(JCR *jcr)
       dcr = find_device(jcr, devname, drive);
       if (dcr) {
          dev = dcr->dev;
-         dev->lock();                 /* Use P to avoid indefinite block */
+         dev->dlock();                 /* Use P to avoid indefinite block */
          Dmsg1(100, "mount cmd blocked=%d\n", dev->blocked());
          switch (dev->blocked()) {         /* device blocked? */
          case BST_WAITING_FOR_SYSOP:
@@ -726,7 +747,7 @@ static bool mount_cmd(JCR *jcr)
             bnet_fsend(dir, _("3905 Bizarre wait state %d\n"), dev->blocked());
             break;
          }
-         dev->unlock();
+         dev->dunlock();
          free_dcr(dcr);
          jcr->dcr = NULL;
       } else {
@@ -755,7 +776,7 @@ static bool unmount_cmd(JCR *jcr)
       dcr = find_device(jcr, devname, drive);
       if (dcr) {
          dev = dcr->dev;
-         dev->lock();                 /* Use P to avoid indefinite block */
+         dev->dlock();                 /* Use P to avoid indefinite block */
          if (!dev->is_open()) {
             if (!dev->is_busy()) {
                unload_autochanger(dcr, -1);          
@@ -817,7 +838,7 @@ static bool unmount_cmd(JCR *jcr)
                   dev->print_name());
             }
          }
-         dev->unlock();
+         dev->dunlock();
          free_dcr(dcr);
          jcr->dcr = NULL;
       } else {
@@ -851,7 +872,7 @@ static bool release_cmd(JCR *jcr)
       dcr = find_device(jcr, devname, drive);
       if (dcr) {
          dev = dcr->dev;
-         dev->lock();                 /* Use P to avoid indefinite block */
+         dev->dlock();                 /* Use P to avoid indefinite block */
          if (!dev->is_open()) {
             if (!dev->is_busy()) {
                unload_autochanger(dcr, -1);
@@ -890,7 +911,7 @@ static bool release_cmd(JCR *jcr)
             bnet_fsend(dir, _("3022 Device %s released.\n"), 
                dev->print_name());
          }
-         dev->unlock();
+         dev->dunlock();
          free_dcr(dcr);
          jcr->dcr = NULL;
       } else {
@@ -942,7 +963,7 @@ static bool changer_cmd(JCR *jcr)
       dcr = find_device(jcr, devname, -1);
       if (dcr) {
          dev = dcr->dev;
-         dev->lock();                 /* Use P to avoid indefinite block */
+         dev->dlock();                 /* Use P to avoid indefinite block */
          if (!dev->device->changer_res) {
             bnet_fsend(dir, _("3995 Device %s is not an autochanger.\n"), 
                dev->print_name());
@@ -954,7 +975,7 @@ static bool changer_cmd(JCR *jcr)
          } else {                     /* device not being used */
             autochanger_cmd(dcr, dir, cmd);
          }
-         dev->unlock();
+         dev->dunlock();
          free_dcr(dcr);
          jcr->dcr = NULL;
       } else {
@@ -986,7 +1007,7 @@ static bool readlabel_cmd(JCR *jcr)
       dcr = find_device(jcr, devname, drive);
       if (dcr) {
          dev = dcr->dev;
-         dev->lock();                 /* Use P to avoid indefinite block */
+         dev->dlock();                 /* Use P to avoid indefinite block */
          if (!dev->is_open()) {
             read_volume_label(jcr, dev, Slot);
             dev->close();
@@ -998,7 +1019,7 @@ static bool readlabel_cmd(JCR *jcr)
          } else {                     /* device not being used */
             read_volume_label(jcr, dev, Slot);
          }
-         dev->unlock();
+         dev->dunlock();
          free_dcr(dcr);
          jcr->dcr = NULL;
       } else {
index 063375c54838c6d47981b59994ecf2d8003ac7b2..d99ab2c744581cb0a542b8fefe44ae744033fcfa 100644 (file)
@@ -1,16 +1,7 @@
-/*
- *
- *  label.c  Bacula routines to handle labels
- *
- *   Kern Sibbald, MM
- *
- *
- *   Version $Id$
- */
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
+   Copyright (C) 2000-2007 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.
    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
    Switzerland, email:ftf@fsfeurope.org.
 */
+/*
+ *
+ *  label.c  Bacula routines to handle labels
+ *
+ *   Kern Sibbald, MM
+ *
+ *
+ *   Version $Id$
+ */
 
 #include "bacula.h"                   /* pull in global headers */
 #include "stored.h"                   /* pull in Storage Deamon headers */
@@ -213,7 +213,7 @@ int read_dev_volume_label(DCR *dcr)
    }
 
    dev->set_labeled();               /* set has Bacula label */
-   new_volume(dcr, dev->VolHdr.VolumeName);
+   reserve_volume(dcr, dev->VolHdr.VolumeName);
 
    /* Compare Volume Names */
    Dmsg2(30, "Compare Vol names: VolName=%s hdr=%s\n", VolName?VolName:"*", dev->VolHdr.VolumeName);
@@ -393,7 +393,7 @@ bool write_new_volume_label_to_dev(DCR *dcr, const char *VolName,
    if (debug_level >= 20)  {
       dump_volume_label(dev);
    }
-   new_volume(dcr, VolName);
+   reserve_volume(dcr, VolName);
    dev->clear_append();               /* remove append since this is PRE_LABEL */
    return true;
 
index e613c3e7411515662e54afddc14326d9001deedf..ffa858209b945fb639bc8919ccf052a6f0bb777f 100644 (file)
@@ -1,15 +1,7 @@
-/*
- *   Match Bootstrap Records (used for restores) against
- *     Volume Records
- *
- *     Kern Sibbald, June MMII
- *
- *   Version $Id$
- */
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2002-2006 Free Software Foundation Europe e.V.
+   Copyright (C) 2002-2007 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.
    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
    Switzerland, email:ftf@fsfeurope.org.
 */
+/*
+ *   Match Bootstrap Records (used for restores) against
+ *     Volume Records
+ *
+ *     Kern Sibbald, June MMII
+ *
+ *   Version $Id$
+ */
 
 /*
  * ***FIXME***
@@ -60,7 +60,7 @@
 #include "lib/fnmatch.h"
 #endif
 
-const int dbglevel = 10;
+const int dbglevel = 500;
 
 /* Forward references */
 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done);
index fdeeada24957946c7bfcdb7fcd8863d53bf7f3f0..b26eb2a5c938bf644efb3e3f7dbaf88befe93be7 100644 (file)
@@ -1,8 +1,3 @@
-/*
- * Protypes for stored -- Kern Sibbald MM  
- *
- *   Version $Id$
- */
 /*
    Bacula® - The Network Backup Solution
 
    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
    Switzerland, email:ftf@fsfeurope.org.
 */
+/*
+ * Protypes for stored -- Kern Sibbald MM  
+ *
+ *   Version $Id$
+ */
 
 /* From stored.c */
 uint32_t new_VolSessionId();
@@ -135,6 +135,7 @@ void     _give_back_device_lock(const char *file, int line, DEVICE *dev, bsteal_
 void     set_new_volume_parameters(DCR *dcr);
 void     set_new_file_parameters(DCR *dcr);
 bool     is_device_unmounted(DEVICE *dev);
+uint32_t get_jobid_from_tid();
 
 /* From dircmd.c */
 void     *handle_connection_request(void *arg);
@@ -210,13 +211,14 @@ bool read_records(DCR *dcr,
 /* From reserve.c */
 void    init_reservations_lock();
 void    term_reservations_lock();
-void    lock_reservations();
-void    unlock_reservations();
+void    _lock_reservations();
+void    _unlock_reservations();
 void    release_volume(DCR *dcr);
-VOLRES *new_volume(DCR *dcr, const char *VolumeName);
+VOLRES *reserve_volume(DCR *dcr, const char *VolumeName);
 VOLRES *find_volume(const char *VolumeName);
 bool    free_volume(DEVICE *dev);
-void    free_unused_volume(DCR *dcr);
+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);
@@ -226,6 +228,25 @@ bool    find_suitable_device_for_job(JCR *jcr, RCTX &rctx);
 int     search_res_for_device(RCTX &rctx);
 void    release_msgs(JCR *jcr);
 
+extern int reservations_lock_count;
+
+#ifdef  SD_DEBUG_LOCK
+#define lock_reservations() \
+         do { Dmsg4(sd_dbglvl, "lock_reservations at %s:%d precnt=%d JobId=%u\n", \
+              __FILE__, __LINE__, \
+              reservations_lock_count, get_jobid_from_tid()); \
+                   _lock_reservations(); } while (0)
+#define unlock_reservations() \
+         do { Dmsg4(sd_dbglvl, "unlock_reservations at %s:%d precnt=%d JobId=%u\n", \
+              __FILE__, __LINE__, \
+              reservations_lock_count, get_jobid_from_tid()); \
+                   _unlock_reservations(); } while (0)
+#else
+#define lock_reservations() _lock_reservations()
+#define unlock_reservations() _unlock_reservations()
+#endif
+
+
 
 /* From spool.c */
 bool    begin_data_spool          (DCR *dcr);
index 79d299dd2912ee21bfc652ab55e383dfb33f03f3..bc590e210ad2a010bc1882951b76104cd38cfbd9 100644 (file)
@@ -96,10 +96,13 @@ void term_reservations_lock()
    rwl_destroy(&reservation_lock);
 }
 
+int reservations_lock_count = 0;
+
 /* This applies to a drive and to Volumes */
-void lock_reservations()
+void _lock_reservations()
 {
    int errstat;
+   reservations_lock_count++;
    if ((errstat=rwl_writelock(&reservation_lock)) != 0) {
       berrno be;
       Emsg2(M_ABORT, 0, "rwl_writelock failure. stat=%d: ERR=%s\n",
@@ -107,9 +110,10 @@ void lock_reservations()
    }
 }
 
-void unlock_reservations()
+void _unlock_reservations()
 {
    int errstat;
+   reservations_lock_count--;
    if ((errstat=rwl_writeunlock(&reservation_lock)) != 0) {
       berrno be;
       Emsg2(M_ABORT, 0, "rwl_writeunlock failure. stat=%d: ERR=%s\n",
@@ -117,20 +121,156 @@ void unlock_reservations()
    }
 }
 
+/*
+ * List Volumes -- this should be moved to status.c
+ */
+enum {
+   debug_lock = true,
+   debug_nolock = false
+};
+
+static void debug_list_volumes(const char *imsg, bool do_lock)
+{
+   VOLRES *vol;
+   POOL_MEM msg(PM_MESSAGE);
+   int count = 0;
+   DEVICE *dev = NULL;
+
+   if (do_lock) P(vol_list_lock);
+   for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) {
+      if (vol->dev) {
+         Mmsg(msg, "List from %s: %s at %p on device %s\n", imsg, 
+              vol->vol_name, vol->vol_name, vol->dev->print_name());
+      } else {
+         Mmsg(msg, "List from %s: %s at %p no dev\n", imsg, vol->vol_name, vol->vol_name);
+      }
+      Dmsg1(100, "%s", msg.c_str());
+      count++;
+   }
+
+   for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) {
+      if (vol->dev == dev) {
+         Dmsg0(000, "Two Volumes on same device.\n");
+         ASSERT(1);
+         dev = vol->dev;
+      }
+   }
+
+   Dmsg2(100, "List from %s: %d volumes\n", imsg, count);
+   if (do_lock) V(vol_list_lock);
+}
+
+
+/*
+ * 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;
+
+   P(vol_list_lock);
+   for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) {
+      if (vol->dev) {
+         len = Mmsg(msg, "%s on device %s\n", vol->vol_name, vol->dev->print_name());
+         sendit(msg.c_str(), len, arg);
+      } else {
+         len = Mmsg(msg, "%s no dev\n", vol->vol_name);
+         sendit(msg.c_str(), len, arg);
+      }
+   }
+   V(vol_list_lock);
+}
+
+/*
+ * 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;
+   Dmsg4(100, "New Vol=%s at %p dev=%s JobId=%u\n", VolumeName, vol->vol_name,
+         vol->dev->print_name(), (int)dcr->jcr->JobId);
+   return vol;
+}
+
+static void free_vol_item(VOLRES *vol)
+{
+   free(vol->vol_name);
+   free(vol);
+}
+
 
 /*
  * 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 (reserved, 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 it needs to be 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 has the job (dcr) owning the volume (more or less).  The job is 
+ *  to change the insertion and removal of the volumes from the list to be based 
+ *  on the drive rather than the job.  The new logic described above needs to be 
+ *  reviewed a couple more times for completeness and correctness.  Then I can 
+ *  program it.
+
  *
  *  Return: VOLRES entry on success
- *          NULL if the Volume is already in the list
+ *          NULL error
  */
-VOLRES *new_volume(DCR *dcr, const char *VolumeName)
+VOLRES *reserve_volume(DCR *dcr, const char *VolumeName)
 {
    VOLRES *vol, *nvol;
+   DEVICE *dev = dcr->dev;
+
+   ASSERT(dev != NULL);
 
-   Dmsg1(400, "new_volume %s\n", VolumeName);
+   Dmsg1(100, "reserve_volume %s\n", VolumeName);
    /* 
     * We lock the reservations system here to ensure
     *  when adding a new volume that no newly scheduled
@@ -138,62 +278,67 @@ VOLRES *new_volume(DCR *dcr, const char *VolumeName)
     */
    lock_reservations();
    P(vol_list_lock);
-   /*
-    * First, if this dcr already is attached to any device,
-    *  remove any old volumes attached to this device as they
-    *  are no longer used (there should at max be one).
+   debug_list_volumes("begin reserve_volume", debug_nolock);
+   /* 
+    * First, remove any old volume attached to this device as it
+    *  is no longer used.
     */
-   if (dcr->dev) {
-again:
-      foreach_dlist(vol, vol_list) {
-         if (vol && vol->dev == dcr->dev) {
-            vol_list->remove(vol);
-            /*
-             * Make sure we don't remove the current volume we are inserting
-             *  because it was probably inserted by another job.
-             */
-            if (vol->vol_name && strcmp(vol->vol_name, VolumeName) != 0) {
-               Dmsg1(100, "new_vol free vol=%s\n", vol->vol_name);
-               free(vol->vol_name);
-            }
-            free(vol);
-            goto again;
-         }
+   if (dev->vol) {
+      vol = dev->vol;
+      /*
+       * Make sure we don't remove the current volume we are inserting
+       *  because it was probably inserted by another job.
+       */
+      if (strcmp(vol->vol_name, VolumeName) == 0) {
+         goto get_out;                  /* Volume already on this device */
+      } else {
+         Dmsg3(100, "reserve_vol free vol=%s at %p JobId=%u\n", vol->vol_name,
+               vol->vol_name, (int)dcr->jcr->JobId);
+         debug_list_volumes("reserve_vol free", debug_nolock);
+         vol_list->remove(vol);
+         free_vol_item(vol);
       }
    }
-   vol = (VOLRES *)malloc(sizeof(VOLRES));
-   memset(vol, 0, sizeof(VOLRES));
-   vol->vol_name = bstrdup(VolumeName);
-   vol->dev = dcr->dev;
-   vol->dcr = dcr;
-   Dmsg2(100, "New Vol=%s dev=%s\n", VolumeName, dcr->dev->print_name());
+
+   /* Create a new Volume entry */
+   nvol = new_vol_item(dcr, VolumeName);
+
    /*
     * Now try to insert the new Volume
     */
-   nvol = (VOLRES *)vol_list->binary_insert(vol, my_compare);
-   if (nvol != vol) {
-      Dmsg2(100, "Found vol=%s same dcr=%d\n", nvol->vol_name, dcr==nvol->dcr);
+   vol = (VOLRES *)vol_list->binary_insert(nvol, my_compare);
+   if (vol != nvol) {
+      Dmsg2(100, "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, free our temp structure
+       * 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.
        */
-      free(vol->vol_name);
-      free(vol);
-      vol = NULL;
-      if (dcr->dev) {
-         DEVICE *dev = nvol->dev;
-         /* ***FIXME*** don't we need a mutex here? */
+      Dmsg3(100, "reserve_vol free-tmp vol=%s at %p JobId=%u\n", vol->vol_name,
+            vol->vol_name, (int)dcr->jcr->JobId);
+      free_vol_item(nvol);
+
+      /* Check if we are trying to use the Volume on a different drive */
+      if (dev != vol->dev) {
+         /* Caller wants to switch Volume to another device */
          if (!dev->is_busy()) {
+            /* OK to move it */
             Dmsg3(100, "Swap vol=%s from dev=%s to %s\n", VolumeName,
-               dev->print_name(), dcr->dev->print_name());
-            nvol->dev = dcr->dev;
+               dev->print_name(), dev->print_name());
+            vol->dev = dev;
             dev->VolHdr.VolumeName[0] = 0;
          } else {
+            vol = NULL;                /* device busy */
             Dmsg3(100, "Logic ERROR!!!! could not swap vol=%s from dev=%s to %s\n", VolumeName,
                dev->print_name(), dcr->dev->print_name());
+            ASSERT(1);     /* blow up!!! */
          }
       }
    }
+   dev->vol = vol;
+
+get_out:
+   debug_list_volumes("end new volume", debug_nolock);
    V(vol_list_lock);
    unlock_reservations();
    return vol;
@@ -213,113 +358,88 @@ VOLRES *find_volume(const char *VolumeName)
    vol.vol_name = bstrdup(VolumeName);
    fvol = (VOLRES *)vol_list->binary_search(&vol, my_compare);
    free(vol.vol_name);
-   V(vol_list_lock);
    Dmsg2(100, "find_vol=%s found=%d\n", VolumeName, fvol!=NULL);
+   debug_list_volumes("find_volume", debug_nolock);
+   V(vol_list_lock);
    return fvol;
 }
 
+/* 
+ * Remove any reservation from a drive and tell the system
+ *  that the volume is unused at least by us.
+ */
+void unreserve_device(DCR *dcr)
+{
+   DEVICE *dev = dcr->dev;
+   dev->dlock();
+   if (dcr->reserved_device) {
+      dcr->reserved_device = false;
+      dev->reserved_device--;
+      Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name());
+      dcr->reserved_device = false;
+      /* If we set read mode in reserving, remove it */
+      if (dev->can_read()) {
+         dev->clear_read();
+      }
+      if (dev->num_writers < 0) {
+         Jmsg1(dcr->jcr, M_ERROR, 0, _("Hey! num_writers=%d!!!!\n"), dev->num_writers);
+         dev->num_writers = 0;
+      }
+   }
+
+   volume_unused(dcr);
+   dev->dunlock();
+}
+
 /*  
- * Free a Volume from the Volume list
+ * Free a Volume from the Volume list if it is no longer used
  *
  *  Returns: true if the Volume found and removed from the list
- *           false if the Volume is not in the list
+ *           false if the Volume is not in the list or is in use
  */
-bool free_volume(DEVICE *dev)
+bool volume_unused(DCR *dcr)
 {
-   VOLRES vol, *fvol;
+   DEVICE *dev = dcr->dev;
 
-   if (dev->VolHdr.VolumeName[0] == 0) {
+   if (dev->vol == NULL) {
+      Dmsg1(100, " unreserve_volume: no vol on %s\n", dev->print_name());
+      debug_list_volumes("null return unreserve_volume", debug_lock);
       return false;
    }
 
-   P(vol_list_lock);
-#ifdef xxx
-      Dmsg1(100, "free_volume: no vol on dev %s\n", dev->print_name());
-      /*
-       * Our device has no VolumeName listed, but
-       *  search the list for any Volume attached to
-       *  this device and remove it.
-       */
-      foreach_dlist(fvol, vol_list) {
-         if (fvol && fvol->dev == dev) {
-            vol_list->remove(fvol);
-            if (fvol->vol_name) {
-               Dmsg2(100, "free_volume %s dev=%s\n", fvol->vol_name, dev->print_name());
-               free(fvol->vol_name);
-            }
-            free(fvol);
-            break;
-         }
-      }
-      goto bail_out;
-   }
-#endif
-   Dmsg1(400, "free_volume %s\n", dev->VolHdr.VolumeName);
-   vol.vol_name = bstrdup(dev->VolHdr.VolumeName);
-   fvol = (VOLRES *)vol_list->binary_search(&vol, my_compare);
-   if (fvol) {
-      vol_list->remove(fvol);
-      Dmsg2(100, "free_volume %s dev=%s\n", fvol->vol_name, dev->print_name());
-      free(fvol->vol_name);
-      free(fvol);
+   if (dev->is_busy()) {
+      Dmsg1(100, "unreserve_volume: dev is busy %s\n", dev->print_name());
+      debug_list_volumes("dev busy return unreserve_volume", debug_lock);
+      return false;
    }
-   free(vol.vol_name);
-//   dev->VolHdr.VolumeName[0] = 0;
-//bail_out:
-   V(vol_list_lock);
-   return fvol != NULL;
-}
 
-/* Free volume reserved by this dcr but not attached to a dev */
-void free_unused_volume(DCR *dcr)
-{
-   VOLRES *vol;
-
-   P(vol_list_lock);
-   for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) {
-      /*
-       * Releease this volume, but only if we inserted it (same dcr) and
-       *  it is not attached to a device or the Volume in the device is
-       *  different. Requiring a different name for the Volume in the
-       *  device ensures that we don't free a volume in use.
-       */
-      if (vol->dcr == dcr && (vol->dev == NULL || 
-          strcmp(vol->vol_name, vol->dev->VolHdr.VolumeName) != 0)) {
-         vol_list->remove(vol);
-         Dmsg1(100, "free_unused_volume %s\n", vol->vol_name);
-         free(vol->vol_name);
-         free(vol);
-         break;
-      }
-   }
-   V(vol_list_lock);
+   return free_volume(dev);
 }
 
 /*
- * List Volumes -- this should be moved to status.c
+ * Unconditionally release the volume
  */
-void list_volumes(void sendit(const char *msg, int len, void *sarg), void *arg)
+bool free_volume(DEVICE *dev)
 {
    VOLRES *vol;
-   char *msg;
-   int len;
 
-   msg = (char *)get_pool_memory(PM_MESSAGE);
-
-   P(vol_list_lock);
-   for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) {
-      if (vol->dev) {
-         len = Mmsg(msg, "%s on device %s\n", vol->vol_name, vol->dev->print_name());
-         sendit(msg, len, arg);
-      } else {
-         len = Mmsg(msg, "%s\n", vol->vol_name);
-         sendit(msg, len, arg);
-      }
+   if (dev->vol == NULL) {
+      Dmsg1(100, "No vol on dev %s\n", dev->print_name());
+      return false;
    }
+   P(vol_list_lock);
+   vol = dev->vol;
+   dev->vol = NULL;
+   Dmsg1(100, "free_volume %s\n", vol->vol_name);
+   vol_list->remove(vol);
+   Dmsg3(100, "free_volume %s at %p dev=%s\n", vol->vol_name, vol->vol_name,
+         dev->print_name());
+   free_vol_item(vol);
+   debug_list_volumes("free_volume", debug_nolock);
    V(vol_list_lock);
-
-   free_pool_memory(msg);
+   return vol != NULL;
 }
+
       
 /* Create the Volume list */
 void create_volume_list()
@@ -339,8 +459,9 @@ void free_volume_list()
    }
    P(vol_list_lock);
    for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) {
-      Dmsg3(100, "Unreleased Volume=%s dcr=0x%x dev=0x%x\n", vol->vol_name,
-         vol->dcr, vol->dev);
+      Dmsg2(100, "Unreleased Volume=%s dev=%p\n", vol->vol_name, vol->dev);
+      free(vol->vol_name);
+      vol->vol_name = NULL;
    }
    delete vol_list;
    vol_list = NULL;
@@ -354,10 +475,8 @@ bool is_volume_in_use(DCR *dcr)
       Dmsg1(100, "Vol=%s not in use.\n", dcr->VolumeName);
       return false;                   /* vol not in list */
    }
-   if (!vol->dev) {                   /* vol not attached to device */
-      Dmsg1(100, "Vol=%s has no dev.\n", dcr->VolumeName);
-      return false;
-   }
+   ASSERT(vol->dev != NULL);
+
    if (dcr->dev == vol->dev) {        /* same device OK */
       Dmsg1(100, "Vol=%s on same dev.\n", dcr->VolumeName);
       return false;
@@ -479,6 +598,7 @@ static bool use_storage_cmd(JCR *jcr)
          }
          rctx.suitable_device = false;
          rctx.have_volume = false;
+         rctx.VolumeName[0] = 0;
          rctx.any_drive = false;
          if (!jcr->PreferMountedVols) {
             /* Look for unused drives in autochangers */
@@ -625,7 +745,7 @@ bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx)
             ok = true;
             break;
          } else if (stat == 0) {      /* device busy */
-            Dmsg1(110, "Suitable device found=%s, not used: busy\n", device_name);
+            Dmsg1(110, "Suitable device=%s, busy: not use\n", device_name);
          } else {
             /* otherwise error */
             Dmsg0(110, "No suitable device found.\n");
@@ -759,14 +879,16 @@ static int reserve_device(RCTX &rctx)
    bstrncpy(dcr->media_type, rctx.store->media_type, name_len);
    bstrncpy(dcr->dev_name, rctx.device_name, name_len);
    if (rctx.store->append == SD_APPEND) {
-      if (rctx.exact_match && !rctx.have_volume) {
+      Dmsg2(100, "have_vol=%d vol=%s\n", rctx.have_volume, rctx.VolumeName);
+      if (!rctx.have_volume) {
          dcr->any_volume = true;
          if (dir_find_next_appendable_volume(dcr)) {
             bstrncpy(rctx.VolumeName, dcr->VolumeName, sizeof(rctx.VolumeName));
-            Dmsg2(100, "JobId=%u looking for Volume=%s\n", rctx.jcr->JobId, rctx.VolumeName);
+            Dmsg2(100, "JobId=%u looking for Volume=%s\n", (int)rctx.jcr->JobId, rctx.VolumeName);
             rctx.have_volume = true;
          } else {
             Dmsg0(100, "No next volume found\n");
+            rctx.have_volume = false;
             rctx.VolumeName[0] = 0;
         }
       }
@@ -787,6 +909,7 @@ static int reserve_device(RCTX &rctx)
       }
    }
    if (!ok) {
+      rctx.have_volume = false;
       free_dcr(dcr);
       Dmsg0(110, "Not OK.\n");
       return 0;
@@ -810,7 +933,7 @@ static bool reserve_device_for_read(DCR *dcr)
 
    /* Get locks in correct order */
    unlock_reservations();
-   dev->lock();  
+   dev->dlock();  
    lock_reservations();
 
    if (is_device_unmounted(dev)) {             
@@ -839,7 +962,7 @@ static bool reserve_device_for_read(DCR *dcr)
    dcr->reserved_device = true;
 
 bail_out:
-   dev->unlock();
+   dev->dunlock();
    return ok;
 }
 
@@ -869,7 +992,7 @@ static bool reserve_device_for_append(DCR *dcr, RCTX &rctx)
 
    /* Get locks in correct order */
    unlock_reservations();
-   dev->lock();
+   dev->dlock();
    lock_reservations();
 
    /* If device is being read, we cannot write it */
@@ -905,7 +1028,7 @@ static bool reserve_device_for_append(DCR *dcr, RCTX &rctx)
    ok = true;
 
 bail_out:
-   dev->unlock();
+   dev->dunlock();
    return ok;
 }
 
index aaaed4d087a8e28f56f1725f85baa73ccf8da3f5..cb2b16ea4d0b27a84835ce87feabb40140514161 100644 (file)
@@ -234,7 +234,7 @@ static bool despool_data(DCR *dcr, bool commit)
    }
    dcr->despool_wait = true;
    dcr->spooling = false;
-   lock_device(dcr->dev);
+   dcr->dev->r_dlock();
    dcr->despool_wait = false;
    dcr->despooling = true;
    dcr->dev_locked = true;
@@ -330,7 +330,7 @@ static bool despool_data(DCR *dcr, bool commit)
    /* If doing a commit, leave the device locked -- unlocked in release_device() */
    if (!commit) {
       dcr->dev_locked = false;
-      dcr->dev->unlock();
+      dcr->dev->dunlock();
    }
    return ok;
 }
index 0c458244cf21a0cc79a4b1f1366bf9ec191b0bcb..a530d9a2a53e7283141ae6a053c40cc1424d1cbe 100644 (file)
@@ -1,8 +1,3 @@
-/*
- * Storage daemon specific defines and includes
- *
- *  Version $Id$
- */
 /*
    Bacula® - The Network Backup Solution
 
    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
    Switzerland, email:ftf@fsfeurope.org.
 */
+/*
+ * Storage daemon specific defines and includes
+ *
+ *  Version $Id$
+ */
 
 #ifndef __STORED_H_
 #define __STORED_H_
 
 #define STORAGE_DAEMON 1
 
+/* Set to debug mutexes */
+#define SD_DEBUG_LOCK
+const int sd_dbglvl = 100;
+
 #ifdef HAVE_MTIO_H
 #include <mtio.h>
 #else
index 6251a9b4ab76a6567ce02473d1f15ccd86b7ecaa..cd034ef1f5050102fb05eb87a611d125409d4e5b 100644 (file)
@@ -61,7 +61,7 @@ int wait_for_sysop(DCR *dcr)
    DEVICE *dev = dcr->dev;
    JCR *jcr = dcr->jcr;
 
-   dev->lock();  
+   dev->dlock();  
    Dmsg1(100, "Enter blocked=%s\n", dev->print_blocked());
    unmounted = is_device_unmounted(dev);
 
@@ -184,7 +184,7 @@ int wait_for_sysop(DCR *dcr)
       Dmsg1(400, "set %s\n", dev->print_blocked());
    }
    Dmsg1(400, "Exit blocked=%s\n", dev->print_blocked());
-   dev->unlock();
+   dev->dunlock();
    return stat;
 }
 
index d94769d5420606d0838115a3fc723d78e7c450fb..408db2e472c83821693abcf2a230420a33c98e57 100644 (file)
@@ -3,9 +3,9 @@
  */
 
 #undef  VERSION
-#define VERSION "2.1.7"
-#define BDATE   "04 April 2007"
-#define LSMDATE "04Apr07"
+#define VERSION "2.1.8"
+#define BDATE   "11 April 2007"
+#define LSMDATE "11Apr07"
 
 #define PROG_COPYRIGHT "Copyright (C) %d-2007 Free Software Foundation Europe e.V.\n"
 #define BYEAR "2007"       /* year for copyright messages in progs */
index 06c694912d0beaceb6560325de5a85688a217e37..c127e47370ef9e5a47ac5bf53e21f834fbaeb28b 100644 (file)
@@ -1,6 +1,17 @@
               Technical notes on version 2.1
 
 General:
+11Apr07
+kes  Add exec external-command [wait-seconds] to bconsole. This
+     executes the external-command.  Note! normally external-command
+     should be enclosed in double quotes.
+kes  Turn the .die command on only if DEVELOPER is defined -- i.e.
+     it should normally be off in a production system.
+10Apr07
+kes  Implement die command for SD so that we can force it to dump.
+kes  Implement SD lock debug code.
+kes  Implement new algorithm for keeping Volume list in SD.  It
+     is now owned by the device.
 04Apr07
 kes  Implement new code for freeing in use volumes that should 
      resolve if not all, some of the problems of multiple drive