Release Notes for Bacula 1.33
 
-  Bacula code: Total files = 271 Total lines = 82,422 (*.h *.c *.in)
+  Bacula code: Total files = 281 Total lines = 83,612 (*.h *.c *.in)
 
 Most Significant Changes since 1.32d
 - Implement "update slots scan" that reads the volume label(s).
 
 - Add subsections to the Disaster Recovery index section.
                 
 For 1.33
+- Release SQLite 2.8.8
 - If a tape is recycled while it is mounted, Stanislav Tvrudy must do an
   additional mount to deblock the job.
-- Notes for integrating Nic's code:
-  - Most likely jcr crash is the free_jcr(). The ref count was not incremented
-    in the watchdog call to cancel_job() as it is in the UA -- see
-    dird/job.c.
-  - The ua->jcr = control_jcr in job_monitor_watchdog() dird/job.c doesn't
-    look right.
-  - Add: fd->timer_start = 0;   /* turn off timer */
-    just before "fd->timed_out = TRUE;"in lib/jcr.c jcr_timeout_check.
+- Notes for final checking of Nic's code:
+  Could I get you to double check the switch () statements in the
+  job_check_maxwaittime and job_check_maxruntime functions in
+  src/dird/job.c?
 - In restore, provide option for limiting to a particular Pool.
 - In restore, list FileSets that only have different base names --
   i.e. any FileSet with the same name should be treated as the same.
 
    unlock_jcr_chain();
 }
 
+/*
+ * Check if the maxwaittime has expired and it is possible
+ *  to cancel the job.
+ */
 static bool job_check_maxwaittime(JCR *control_jcr, JCR *jcr)
 {
    bool cancel = false;
          "checking status\n",
         jcr->JobId, jcr->Job, jcr->job->MaxWaitTime);
    switch (jcr->JobStatus) {
-      case JS_Created:
-      case JS_Blocked:
+   case JS_Created:
+   case JS_Blocked:
+   case JS_WaitFD:
+   case JS_WaitSD:
+   case JS_WaitStoreRes:
+   case JS_WaitClientRes:
+   case JS_WaitJobRes:
+   case JS_WaitPriority:
+   case JS_WaitMaxJobs:
+   case JS_WaitStartTime:
+      cancel = true;
+      Dmsg0(200, "JCR blocked in #1\n");
+      break;
+   case JS_Running:
+      Dmsg0(200, "JCR running, checking SD status\n");
+      switch (jcr->SDJobStatus) {
+      case JS_WaitMount:
+      case JS_WaitMedia:
       case JS_WaitFD:
-      case JS_WaitSD:
-      case JS_WaitStoreRes:
-      case JS_WaitClientRes:
-      case JS_WaitJobRes:
-      case JS_WaitPriority:
-      case JS_WaitMaxJobs:
-      case JS_WaitStartTime:
         cancel = true;
-         Dmsg0(200, "JCR blocked in #1\n");
-        break;
-      case JS_Running:
-         Dmsg0(200, "JCR running, checking SD status\n");
-        switch (jcr->SDJobStatus) {
-           case JS_WaitMount:
-           case JS_WaitMedia:
-           case JS_WaitFD:
-              cancel = true;
-               Dmsg0(200, "JCR blocked in #2\n");
-              break;
-           default:
-               Dmsg0(200, "JCR not blocked in #2\n");
-              break;
-        }
-        break;
-      case JS_Terminated:
-      case JS_ErrorTerminated:
-      case JS_Canceled:
-         Dmsg0(200, "JCR already dead in #3\n");
+         Dmsg0(200, "JCR blocked in #2\n");
         break;
       default:
-         Emsg1(M_ABORT, 0, _("Unhandled job status code %d\n"),
-              jcr->JobStatus);
+         Dmsg0(200, "JCR not blocked in #2\n");
+        break;
+      }
+      break;
+   case JS_Terminated:
+   case JS_ErrorTerminated:
+   case JS_Canceled:
+   case JS_FatalError:
+      Dmsg0(200, "JCR already dead in #3\n");
+      break;
+   default:
+      Jmsg1(jcr, M_ERROR, 0, _("Unhandled job status code %d\n"),
+           jcr->JobStatus);
    }
    Dmsg3(200, "MaxWaitTime result: %scancel JCR %p (%s)\n",
          cancel ? "" : "do not ", jcr, jcr->job);
    return cancel;
 }
 
+/*
+ * Check if maxruntime has expired and if the job can be
+ *   canceled.
+ */
 static bool job_check_maxruntime(JCR *control_jcr, JCR *jcr)
 {
    bool cancel = false;
    }
 
    switch (jcr->JobStatus) {
-      case JS_Created:
-      case JS_Blocked:
-      case JS_WaitFD:
-      case JS_WaitSD:
-      case JS_WaitStoreRes:
-      case JS_WaitClientRes:
-      case JS_WaitJobRes:
-      case JS_WaitPriority:
-      case JS_WaitMaxJobs:
-      case JS_WaitStartTime:
-      case JS_Running:
-        cancel = true;
-        break;
-      case JS_Terminated:
-      case JS_ErrorTerminated:
-      case JS_Canceled:
-        cancel = false;
-        break;
-      default:
-         Emsg1(M_ABORT, 0, _("Unhandled job status code %d\n"),
-              jcr->JobStatus);
+   case JS_Created:
+   case JS_Running:
+   case JS_Blocked:
+   case JS_WaitFD:
+   case JS_WaitSD:
+   case JS_WaitStoreRes:
+   case JS_WaitClientRes:
+   case JS_WaitJobRes:
+   case JS_WaitPriority:
+   case JS_WaitMaxJobs:
+   case JS_WaitStartTime:
+   case JS_Differences:
+      cancel = true;
+      break;
+   case JS_Terminated:
+   case JS_ErrorTerminated:
+   case JS_Canceled:
+   case JS_FatalError:
+      cancel = false;
+      break;
+   default:
+      Jmsg1(jcr, M_ERROR, 0, _("Unhandled job status code %d\n"),
+           jcr->JobStatus);
    }
 
    Dmsg3(200, "MaxRunTime result: %scancel JCR %p (%s)\n",
 
       Dmsg1(100, "Prepended job=%d to ready queue\n", jcr->JobId);
    } else {
       /* Add this job to the wait queue in priority sorted order */
-      for (li=NULL; (li=(jobq_item_t *)jq->waiting_jobs->next(li)); ) {
+      foreach_dlist (li, jq->waiting_jobs) {
          Dmsg2(100, "waiting item jobid=%d priority=%d\n",
            li->jcr->JobId, li->jcr->JobPriority);
         if (li->jcr->JobPriority > jcr->JobPriority) {
       return stat;
    }
 
-   for (item=NULL; (item=(jobq_item_t *)jq->waiting_jobs->next(item)); ) {
+   foreach_dlist (item, jq->waiting_jobs) {
       if (jcr == item->jcr) {
         found = true;
         break;
 
 
 char *uar_last_full =
    "INSERT INTO temp1 SELECT Job.JobId,JobTdate "
-   "FROM Client,Job,JobMedia,Media WHERE Client.ClientId=%u "
+   "FROM Client,Job,JobMedia,Media,FileSet WHERE Client.ClientId=%u "
    "AND Job.ClientId=%u "
    "AND Job.StartTime<'%s' "
    "AND Level='F' AND JobStatus='T' "
    "AND JobMedia.JobId=Job.JobId "
    "AND JobMedia.MediaId=Media.MediaId "
-   "AND Job.FileSetId=%u "
+   "AND Job.FileSetId=FileSet.FileSetId "
+   "AND FileSet.FileSet='%s' "
    "ORDER BY Job.JobTDate DESC LIMIT 1";
 
 char *uar_full = 
    "INSERT INTO temp SELECT Job.JobId,Job.JobTDate,Job.ClientId,"
    "Job.Level,Job.JobFiles,Job.StartTime,Media.VolumeName,JobMedia.StartFile,"
    "Job.VolSessionId,Job.VolSessionTime "
-   "FROM Job,JobMedia,Media "
+   "FROM Job,JobMedia,Media,FileSet "
    "WHERE Job.JobTDate>%s AND Job.StartTime<'%s' "
    "AND Job.ClientId=%u "
    "AND JobMedia.JobId=Job.JobId "
    "AND JobMedia.MediaId=Media.MediaId "
    "AND Job.Level IN ('I', 'D') AND JobStatus='T' "
-   "AND Job.FileSetId=%u ";
+   "AND Job.FileSetId=FileSet.FileSetId "
+   "AND FileSet.FileSet='%s' ";
 
 char *uar_list_temp = 
    "SELECT JobId,Level,JobFiles,StartTime,VolumeName,StartFile,"
 
 char *uar_sel_all_temp1 = "SELECT * FROM temp1";
 
-/* Select filesets for this Client */
+/* Select FileSet names for this Client */
 char *uar_sel_fileset = 
-   "SELECT DISTINCT FileSet.FileSetId,FileSet.FileSet,FileSet.CreateTime FROM Job,"
+   "SELECT DISTINCT FileSet.FileSet FROM Job,"
    "Client,FileSet WHERE Job.FileSetId=FileSet.FileSetId "
    "AND Job.ClientId=%u AND Client.ClientId=%u "
-   "ORDER BY FileSet.CreateTime";
+   "ORDER BY FileSet.FileSet";
 
 /* Find MediaType used by this Job */
 char *uar_mediatype =
 
    }
 
    if (!open_db(ua)) {
-      free_rx(&rx);
-      return 0;
+      goto bail_out;
    }
 
    /* Ensure there is at least one Restore Job */
       bsendmsg(ua, _(
          "No Restore Job Resource found. You must create at least\n"
          "one before running this command.\n"));
-      free_rx(&rx);
-      return 0;
+      goto bail_out;
    }
 
    /* 
     */
    switch (user_select_jobids_or_files(ua, &rx)) {
    case 0:
-      free_rx(&rx);
-      return 0;                      /* error */
+      goto bail_out;
    case 1:                           /* select by jobid */
       build_directory_tree(ua, &rx);
       break;
    if (rx.bsr->JobId) {
       if (!complete_bsr(ua, rx.bsr)) {  /* find Vol, SessId, SessTime from JobIds */
          bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
-        free_rx(&rx);
-        return 0;
+        goto bail_out;
       }
       write_bsr_file(ua, rx.bsr);
       bsendmsg(ua, _("\n%u file%s selected to be restored.\n\n"), rx.selected_files,
          rx.selected_files==1?"":"s");
    } else {
       bsendmsg(ua, _("No files selected to be restored.\n"));
-      free_rx(&rx);
-      return 0;
+      goto bail_out;
    }
 
    if (rx.restore_jobs == 1) {
       job = select_restore_job_resource(ua);
    }
    if (!job) {
-      bsendmsg(ua, _("No Restore Job resource found!\n"));
-      free_rx(&rx);
-      return 0;
+      goto bail_out;
    }
 
    get_client_name(ua, &rx);
+   if (!rx.ClientName) {
+      bsendmsg(ua, _("No Restore Job resource found!\n"));
+      goto bail_out;
+   }
 
    /* Build run command */
    if (rx.where) {
    bsendmsg(ua, _("Restore command done.\n"));
    free_rx(&rx);
    return 1;
+
+bail_out:
+   free_rx(&rx);
+   return 0;
+
 }
 
 static void free_rx(RESTORE_CTX *rx) 
       }
       memset(&cr, 0, sizeof(cr));
       if (!get_client_dbr(ua, &cr)) {
-        free_rx(rx);
         return 0;
       }
       bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
    }
    rx->selected_files++;
    /*
-    * Find the FileSets for this JobId and add to the name_list
+    * Find the MediaTypes for this JobId and add to the name_list
     */
    Mmsg(&rx->query, uar_mediatype, rx->JobId);
    if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
          bsendmsg(ua, "%s", db_strerror(ua->db));
       }
       /*
-       * Find the FileSets for this JobId and add to the name_list
+       * Find the MediaTypes for this JobId and add to the name_list
        */
       Mmsg(&rx->query, uar_mediatype, JobId);
       if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
    CLIENT_DBR cr;
    char fileset_name[MAX_NAME_LENGTH];
    char ed1[50];
+   int i;
 
 
    /* Create temp tables */
    bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
 
    /*
-    * Select FileSet 
+    * Get FileSet 
     */
-   Mmsg(&rx->query, uar_sel_fileset, cr.ClientId, cr.ClientId);
-   start_prompt(ua, _("The defined FileSet resources are:\n"));
-   if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
-      bsendmsg(ua, "%s\n", db_strerror(ua->db));
+   memset(&fsr, 0, sizeof(fsr));
+   i = find_arg_with_value(ua, "FileSet"); 
+   if (i >= 0) {
+      bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
+      if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
+         bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet, 
+           db_strerror(ua->db));
+        i = -1;
+      }
    }
-   if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"), 
+   if (i < 0) {                      /* fileset not found */
+      Mmsg(&rx->query, uar_sel_fileset, cr.ClientId, cr.ClientId);
+      start_prompt(ua, _("The defined FileSet resources are:\n"));
+      if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
+         bsendmsg(ua, "%s\n", db_strerror(ua->db));
+      }
+      if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"), 
                 fileset_name, sizeof(fileset_name)) < 0) {
-      goto bail_out;
-   }
-   fsr.FileSetId = atoi(fileset_name); /* Id is first part of name */
-   if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
-      bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
-      bsendmsg(ua, _("This probably means you modified the FileSet.\n"
+        goto bail_out;
+      }
+
+      bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
+      if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
+         bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
+         bsendmsg(ua, _("This probably means you modified the FileSet.\n"
                      "Continuing anyway.\n"));
+      }
    }
 
 
    /* Find JobId of last Full backup for this client, fileset */
-   Mmsg(&rx->query, uar_last_full, cr.ClientId, cr.ClientId, date, fsr.FileSetId);
+   Mmsg(&rx->query, uar_last_full, cr.ClientId, cr.ClientId, date, fsr.FileSet);
    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
       bsendmsg(ua, "%s\n", db_strerror(ua->db));
       goto bail_out;
 
    /* Now find all Incremental/Decremental Jobs after Full save */
    Mmsg(&rx->query, uar_inc_dec, edit_uint64(rx->JobTDate, ed1), date,
-       cr.ClientId, fsr.FileSetId);
+       cr.ClientId, fsr.FileSet);
    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
       bsendmsg(ua, "%s\n", db_strerror(ua->db));
    }
    return stat;
 }
 
+
 /* Return next JobId from comma separated list */
 static int next_jobid_from_list(char **p, uint32_t *JobId)
 {
 }
 
 /*
- * Callback handler build fileset prompt list
+ * Callback handler build FileSet name prompt list
  */
 static int fileset_handler(void *ctx, int num_fields, char **row)
 {
-   char prompt[MAX_NAME_LENGTH+200];
 
-   snprintf(prompt, sizeof(prompt), "%s  %s  %s", row[0], row[1], row[2]);
-   add_prompt((UAContext *)ctx, prompt);
+   /* row[0] = FileSet (name) */
+   if (row[0]) {
+      add_prompt((UAContext *)ctx, row[0]);
+   }
    return 0;
 }
 
 
 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
 {
-   char name[MAX_NAME_LENGTH];
-   STORE *store = NULL;
-
    if (name_list->num_ids > 1) {
       bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
          "Restore is not possible. The MediaTypes used are:\n"));
       return;
    }
 
-   start_prompt(ua, _("The defined Storage resources are:\n"));
-   LockRes();
-   while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
-      if (strcmp(store->media_type, name_list->name[0]) == 0) {
-        add_prompt(ua, store->hdr.name);
-      }
-   }
-   UnlockRes();
-   do_prompt(ua, _("Storage"),  _("Select Storage resource"), name, sizeof(name));
-   rx->store = (STORE *)GetResWithName(R_STORAGE, name);
+   rx->store = get_storage_resource(ua, false /* don't use default */);
+
    if (!rx->store) {
       bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
          "MediaType %s, needed by the Jobs you selected.\n"
 
          */
         if (jcr->JobType == JT_CONSOLE) {
            bstrftime(dt, sizeof(dt), jcr->start_time);
-           strcpy(dt+7, dt+9);  /* cut century */
-           bsendmsg(ua, _("Console connected at %s\n"), dt);
+           strcpy(dt+7, dt+9);  /* cut century */
+            bsendmsg(ua, _("Console connected at %s\n"), dt);
         }
         njobs--;
       }
    lock_last_jobs_list();
    struct s_last_job *je;
    bsendmsg(ua, _("\nTerminated Jobs:\n"));
-   bsendmsg(ua, _(" JobId  Level   Files        Bytes Status   Finished        Name \n"));
-   bsendmsg(ua, _("====================================================================\n"));
-   for (je=NULL; (je=(s_last_job *)last_jobs->next(je)); ) {
+   bsendmsg(ua, _(" JobId  Level   Files          Bytes Status   Finished        Name \n"));
+   bsendmsg(ua, _("======================================================================\n"));
+   foreach_dlist(je, last_jobs) {
       char JobName[MAX_NAME_LENGTH];
       char *termstat;
 
            *p = 0;
         }
       }
-      bsendmsg(ua, _("%6d  %-4s %8s %12s %-7s  %-8s %s\n"), 
+      bsendmsg(ua, _("%6d  %-4s %8s %14s %-7s  %-8s %s\n"), 
         je->JobId,
         level, 
         edit_uint64_with_commas(je->JobFiles, b1),
 
    lock_last_jobs_list();
    msg =  _("\nTerminated Jobs:\n"); 
    sendit(msg, strlen(msg), arg);
-   msg =  _(" JobId  Level   Files        Bytes Status   Finished        Name \n");
+   msg =  _(" JobId  Level   Files          Bytes Status   Finished        Name \n");
    sendit(msg, strlen(msg), arg);
-   msg = _("====================================================================\n"); 
+   msg = _("======================================================================\n"); 
    sendit(msg, strlen(msg), arg);
    for (je=NULL; (je=(s_last_job *)last_jobs->next(je)); ) {
       char JobName[MAX_NAME_LENGTH];
            *p = 0;
         }
       }
-      bsnprintf(buf, sizeof(buf), _("%6d  %-4s %8s %12s %-7s  %-8s %s\n"), 
+      bsnprintf(buf, sizeof(buf), _("%6d  %-4s %8s %14s %-7s  %-8s %s\n"), 
         je->JobId,
         level, 
         edit_uint64_with_commas(je->JobFiles, b1),
 
        fi; \
        echo "${INSTALL_CONFIG} $$srcconf ${DESTDIR}${sysconfdir}/$$destconf"; \
        ${INSTALL_CONFIG} $$srcconf ${DESTDIR}${sysconfdir}/$$destconf
+       if test -f static-gnome-console; then \
+          $(INSTALL_SCRIPT) static-gnome-console $(DESTDIR)$(sbindir)/static-gnome-console; \
+       fi 
 
 uninstall:
-       (cd $(DESTDIR)$(sbindir); $(RMF) gnome-console)
+       (cd $(DESTDIR)$(sbindir); $(RMF) gnome-console static-gnome-console)
        (cd $(DESTDIR)$(sysconfdir); $(RMF) gnome-console.conf gnome-console.conf.new)
 
 
 
       sprintf(buf, "This is item %d", i);
       fileset->mylist.append(bstrdup(buf));
    } 
-   for (int i=0; i<fileset->mylist.size(); i++) {
+   for (int i=0; i< fileset->mylist.size(); i++) {
       printf("Item %d = %s\n", i, (char *)fileset->mylist[i]);  
    }
    fileset->mylist.destroy();
       sprintf(buf, "This is item %d", i);
       mlist->append(bstrdup(buf));
    } 
-   for (int i=0; i<mlist->size(); i++) {
+   for (int i=0; i< mlist->size(); i++) {
       printf("Item %d = %s\n", i, (char *)mlist->get(i));  
    }
 
 
    jcr_chain->insert_before(jcr, next_jcr);
    
    printf("Print remaining list.\n");
-   for (MYJCR *jcr=NULL; (jcr=(MYJCR *)jcr_chain->next(jcr)); ) {
+   foreach_dlist (jcr, jcr_chain) {
       printf("Dlist item = %s\n", jcr->buf);
       free(jcr->buf);
    }
    jcr_chain->insert_before(jcr, next_jcr);
    
    printf("Print remaining list.\n");
-   for (MYJCR *jcr=NULL; (jcr=(MYJCR *)jcr_chain->next(jcr)); ) {
+   foreach_dlist (jcr, jcr_chain) {
       printf("Dlist item = %s\n", jcr->buf);
       free(jcr->buf);
    }
 
  *
  */
 
-#define OFFSET(item,link) ((char *)link - (char *)item)
+/* In case you want to specifically specify the offset to the link */
+#define OFFSET(item, link) ((char *)(link) - (char *)(item))
+
+/*
+ * Loop var through each member of list
+ */
+#define foreach_dlist(var, list) \
+        for((var)=NULL; ((void *)(var))=(list)->next((var)); )
 
 struct dlink {
    void *next;
 
 
    jcrtbl->stats();
    printf("Walk the hash table:\n");
-   for (MYJCR *jcr=(MYJCR *)jcrtbl->first(); jcr; jcr=(MYJCR *)jcrtbl->next() ) {
+   foreach_htable (jcr, jcrtbl) {
 //    printf("htable item = %s\n", jcr->key);
       free(jcr->key);
       count++;
 
  *
  */
 
+/*
+ * Loop var through each member of table
+ */
+#define foreach_htable(var, tbl) \
+        for(((void *)(var))=(tbl)->first(); \
+            (var); \
+           ((void *)(var))=(tbl)->next())
+
 struct hlink {
-   void *next;                        /* next hash item */
-   char *key;                         /* key this item */
-   uint32_t hash;                     /* hash for this key */
+   void *next;                       /* next hash item */
+   char *key;                        /* key this item */
+   uint32_t hash;                    /* hash for this key */
 };
 
 class htable {
-   hlink **table;                     /* hash table */
-   int loffset;                       /* link offset in item */
-   uint32_t num_items;                /* current number of items */
-   uint32_t max_items;                /* maximum items before growing */
-   uint32_t buckets;                  /* size of hash table */
-   uint32_t hash;                     /* temp storage */
-   uint32_t index;                    /* temp storage */
+   hlink **table;                    /* hash table */
+   int loffset;                      /* link offset in item */
+   uint32_t num_items;               /* current number of items */
+   uint32_t max_items;               /* maximum items before growing */
+   uint32_t buckets;                 /* size of hash table */
+   uint32_t hash;                    /* temp storage */
+   uint32_t index;                   /* temp storage */
    uint32_t mask;                     /* "remainder" mask */
-   uint32_t rshift;                   /* amount to shift down */
-   hlink *walkptr;                    /* table walk pointer */
-   uint32_t walk_index;               /* table walk index */
-   void hash_index(char *key);        /* produce hash key,index */
-   void grow_table();                 /* grow the table */
+   uint32_t rshift;                  /* amount to shift down */
+   hlink *walkptr;                   /* table walk pointer */
+   uint32_t walk_index;              /* table walk index */
+   void hash_index(char *key);       /* produce hash key,index */
+   void grow_table();                /* grow the table */
 public:
    htable(void *item, void *link, int tsize = 31);
    void init(void *item, void *link, int tsize = 31);
    bool  insert(char *key, void *item);
    void *lookup(char *key);
-   void *first();                     /* get first item in table */
-   void *next();                      /* get next item in table */
+   void *first();                    /* get first item in table */
+   void *next();                     /* get next item in table */
    void destroy();
-   void stats();                      /* print stats about the table */
-   uint32_t size();                   /* return size of table */
+   void stats();                     /* print stats about the table */
+   uint32_t size();                  /* return size of table */
    void * operator new(size_t);
    void operator delete(void *);
 };
 
 {
    JCR *jcr;
    BSOCK *fd;
-   time_t timer_start, now;
+   time_t timer_start;
 
    Dmsg0(200, "Start JCR timeout checks\n");
 
 
 static bool wd_is_init = false;
 
 /* Forward referenced callback functions */
-static void callback_child_timer(watchdog_t *self);
-static void callback_thread_timer(watchdog_t *self);
 static pthread_t wd_tid;
 
 /* Static globals */
    wd->next_fire = watchdog_time + wd->interval;
    TAILQ_INSERT_TAIL(&wd_queue, wd, qe);
    Dmsg3(200, "Registered watchdog %p, interval %d%s\n",
-        wd, wd->interval, wd->one_shot ? " one shot" : "");
+         wd, wd->interval, wd->one_shot ? " one shot" : "");
    V(mutex);
 
    return false;
    TAILQ_FOREACH_SAFE(p, &wd_queue, qe, n) {
       if (wd == p) {
         TAILQ_REMOVE(&wd_queue, wd, qe);
-        Dmsg1(200, "Unregistered watchdog %p\n", wd);
+         Dmsg1(200, "Unregistered watchdog %p\n", wd);
         return true;
       }
    }
    TAILQ_FOREACH_SAFE(p, &wd_inactive, qe, n) {
       if (wd == p) {
         TAILQ_REMOVE(&wd_inactive, wd, qe);
-        Dmsg1(200, "Unregistered inactive watchdog %p\n", wd);
+         Dmsg1(200, "Unregistered inactive watchdog %p\n", wd);
         return true;
       }
    }
            /* Run the callback */
            p->callback(p);
 
-           /* Reschedule (or move to inactive list if it's a one-shot timer) */
+            /* Reschedule (or move to inactive list if it's a one-shot timer) */
            if (p->one_shot) {
               TAILQ_REMOVE(&wd_queue, p, qe);
               TAILQ_INSERT_TAIL(&wd_inactive, p, qe);
 
    lock_last_jobs_list();
    msg =  _("\nTerminated Jobs:\n"); 
    sendit(msg, strlen(msg), arg);
-   msg =  _(" JobId  Level   Files        Bytes Status   Finished        Name \n");
+   msg =  _(" JobId  Level   Files          Bytes Status   Finished        Name \n");
    sendit(msg, strlen(msg), arg);
-   msg = _("====================================================================\n"); 
+   msg = _("======================================================================\n"); 
    sendit(msg, strlen(msg), arg);
    for (je=NULL; (je=(s_last_job *)last_jobs->next(je)); ) {
       char JobName[MAX_NAME_LENGTH];
            *p = 0;
         }
       }
-      bsnprintf(buf, sizeof(buf), _("%6d  %-4s %8s %12s %-7s  %-8s %s\n"), 
+      bsnprintf(buf, sizeof(buf), _("%6d  %-4s %8s %14s %-7s  %-8s %s\n"), 
         je->JobId,
         level, 
         edit_uint64_with_commas(je->JobFiles, b1),
 
    {"autochanger",           store_yesno,  ITEM(res_dev.cap_bits), CAP_AUTOCHANGER, ITEM_DEFAULT, 0},
    {"changerdevice",         store_strname,ITEM(res_dev.changer_name), 0, 0, 0},
    {"changercommand",        store_strname,ITEM(res_dev.changer_command), 0, 0, 0},
-   {"maximumchangerwait",    store_pint,   ITEM(res_dev.max_changer_wait), 0, ITEM_DEFAULT, 2 * 60},
+   {"maximumchangerwait",    store_pint,   ITEM(res_dev.max_changer_wait), 0, ITEM_DEFAULT, 5 * 60},
    {"maximumopenwait",       store_pint,   ITEM(res_dev.max_open_wait), 0, ITEM_DEFAULT, 5 * 60},
    {"maximumopenvolumes",    store_pint,   ITEM(res_dev.max_open_vols), 0, ITEM_DEFAULT, 1},
    {"offlineonunmount",      store_yesno,  ITEM(res_dev.cap_bits), CAP_OFFLINEUNMOUNT, ITEM_DEFAULT, 0},
 
 #undef  VERSION
 #define VERSION "1.33"
 #define VSTRING "1"
-#define BDATE   "22 Dec 2003"
-#define LSMDATE "22Dec03"
+#define BDATE   "24 Dec 2003"
+#define LSMDATE "24Dec03"
 
 /* Debug flags */
 #undef  DEBUG