Kern's ToDo List
-                   06 July 2004
+                   14 July 2004
 
 1.35 Items to do:
-- Require resource names to be unique.
+- Make sure that all errors from libacl are printed.
 - Add new DCR calling sequences everywhere in SD.
   This will permit simultaneous use of multiple 
   devices by a single job.
 - Document a get out of jail procedure if everything breaks if 
   you lost/broke the Catalog -- do the same for "I know my
   file is there how do I get it back?".
-- Look at Win32 inc problem when new directory added files not saved???
 - Make FD run on Win95 if possible:
    The error I when I installed 1.34.2 clients:
    The BACULA-FD file is  
 - Look at adding Client run command that will use the
   port opened by the client.
 - Fix find_device in stored/dircmd.c:462
+- Make entering multiple Storage names in Dir illegal.
 
 Documentation to do: (any release a little bit at a time)
 - Document query file format.
 - Test new despooling error recovery code when partition fills.
 - Sort Scheduled jobs status listing by start time.
 - Add priority to Scheduled jobs status listing.
+- Look at Win32 inc problem when new directory added files not saved???
+- Require resource names to be unique.
 
 
    msg_type = M_INFO;                /* by default INFO message */
    switch (jcr->JobStatus) {
-      case JS_Terminated:
-         term_msg = _("Admin OK");
-        break;
-      case JS_FatalError:
-      case JS_ErrorTerminated:
-         term_msg = _("*** Admin Error ***"); 
-        msg_type = M_ERROR;          /* Generate error message */
-        break;
-      case JS_Canceled:
-         term_msg = _("Admin Canceled");
-        break;
-      default:
-        term_msg = term_code;
-         sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
-        break;
+   case JS_Terminated:
+      term_msg = _("Admin OK");
+      break;
+   case JS_FatalError:
+   case JS_ErrorTerminated:
+      term_msg = _("*** Admin Error ***"); 
+      msg_type = M_ERROR;         /* Generate error message */
+      break;
+   case JS_Canceled:
+      term_msg = _("Admin Canceled");
+      break;
+   default:
+      term_msg = term_code;
+      sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
+      break;
    }
    bstrftime(sdt, sizeof(sdt), jcr->jr.StartTime);
    bstrftime(edt, sizeof(edt), jcr->jr.EndTime);
 
    {"no",       INC_KW_KEEPATIME,     "0"},
    {"yes",      INC_KW_EXCLUDE,       "e"},
    {"no",       INC_KW_EXCLUDE,       "0"},
-   {"yes",           INC_KW_ACL,        "A"},
-   {"no",           INC_KW_ACL,         "0"},
-   {NULL,      0,                   0}
+   {"yes",      INC_KW_ACL,           "A"},
+   {"no",       INC_KW_ACL,           "0"},
+   {NULL,      0,                      0}
 };
 
 
 
    sm_check(__FILE__, __LINE__, true);
 
    for ( ;; ) {
-
       Dmsg0(200, "=====Start Job=========\n");
       jcr->start_time = time(NULL);     /* set the real start time */
       set_jcr_job_status(jcr, JS_Running);
  */
 void update_job_end_record(JCR *jcr)
 {
-   if (jcr->jr.EndTime == 0) {
-      jcr->jr.EndTime = time(NULL);
-   }
+   jcr->jr.EndTime = time(NULL);
    jcr->end_time = jcr->jr.EndTime;
    jcr->jr.JobId = jcr->JobId;
    jcr->jr.JobStatus = jcr->JobStatus;
 
             jcr->JobStatus != JS_Canceled && 
             jcr->job->RescheduleTimes > 0 && 
             jcr->reschedule_count < jcr->job->RescheduleTimes) {
+            char dt[50];
 
             /*
              * Reschedule this job by cleaning it up, but
            jcr->sched_time = time(NULL) + jcr->job->RescheduleInterval;
             Dmsg2(300, "Rescheduled Job %s to re-run in %d seconds.\n", jcr->Job,
               (int)jcr->job->RescheduleInterval);
+           bstrftime(dt, sizeof(dt), time(NULL));
+            Jmsg(jcr, M_INFO, 0, _("Rescheduled Job %s at %s to re-run in %d seconds.\n"),
+              jcr->Job, dt, (int)jcr->job->RescheduleInterval);
            jcr->JobStatus = JS_Created; /* force new status */
            dird_free_jcr(jcr);          /* partial cleanup old stuff */
            if (jcr->JobBytes == 0) {
 
    "AND Path.Path='%s' "
    "AND Filename.Name='%s' "
    "AND Client.Name='%s' "
+   "AND Job.ClientId=Client.ClientId "
    "AND Path.PathId=File.PathId "
    "AND Filename.FilenameId=File.FilenameId "
    "ORDER BY Job.StartTime DESC LIMIT 1";
 
       mr.InChanger = InChanger;
       Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
       if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
-        bsendmsg(ua, db_strerror(ua->db));
+         bsendmsg(ua, "%s", db_strerror(ua->db));
         return 1;
       }
       if (i == startnum) {
    pr.NumVols += num;
    Dmsg0(200, "Update pool record.\n"); 
    if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
-      bsendmsg(ua, db_strerror(ua->db));
+      bsendmsg(ua, "%s", db_strerror(ua->db));
       return 1;
    }
    bsendmsg(ua, _("%d Volumes created in pool %s\n"), num, pr.Name);
       break;
 
    case -1:
-      bsendmsg(ua, db_strerror(ua->db));
+      bsendmsg(ua, "%s", db_strerror(ua->db));
       break;
 
    default:
 }
 
 /* Modify the Pool in which this Volume is located */
-static void update_volpool(UAContext *ua, char *val, MEDIA_DBR *mr)
+static void update_vol_pool(UAContext *ua, char *val, MEDIA_DBR *mr, POOL_DBR *opr)
 {
    POOL_DBR pr;
    POOLMEM *query;
       bsendmsg(ua, "%s", db_strerror(ua->db));
    } else {      
       bsendmsg(ua, _("New Pool is: %s\n"), pr.Name);
+      opr->NumVols--;
+      if (!db_update_pool_record(ua->jcr, ua->db, opr)) {
+         bsendmsg(ua, "%s", db_strerror(ua->db));
+      }
+      pr.NumVols++;
+      if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
+         bsendmsg(ua, "%s", db_strerror(ua->db));
+      }
+      db_make_inchanger_unique(ua->jcr, ua->db, mr);
    }
-   db_make_inchanger_unique(ua->jcr, ua->db, mr);
    db_unlock(ua->db);
    free_pool_memory(query);
 }
 
+/*
+ * Refresh the Volume information from the Pool record
+ */
 static void update_volfrompool(UAContext *ua, MEDIA_DBR *mr)
 {
    POOL_DBR pr;
 
    for (int i=0; kw[i]; i++) {
       int j;
+      POOL_DBR pr;
       if ((j=find_arg_with_value(ua, kw[i])) > 0) {
         if (!select_media_dbr(ua, &mr)) {
            return 0;
            update_volrecycle(ua, ua->argv[j], &mr);
            break;
         case 7:
-           update_volpool(ua, ua->argv[j], &mr);
+           memset(&pr, 0, sizeof(POOL_DBR));
+           pr.PoolId = mr.PoolId;
+           if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
+               bsendmsg(ua, "%s", db_strerror(ua->db));
+              break;
+           }
+           update_vol_pool(ua, ua->argv[j], &mr, &pr);
            break;
         case 8:
            update_volfrompool(ua, &mr);
          if (!get_cmd(ua, _("Enter new Pool name: "))) {
            return 0;
         }
-        update_volpool(ua, ua->cmd, &mr);
+        update_vol_pool(ua, ua->cmd, &mr, &pr);
         return 1;
 
       default:                       /* Done or error */
 
 /* Forward referenced functions */
 static int do_label(UAContext *ua, const char *cmd, int relabel);
 static void label_from_barcodes(UAContext *ua);
-static int send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr, 
+static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr, 
               POOL_DBR *pr, int relabel, bool media_record_exits);
 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan);
 static void free_vol_list(vol_list_t *vol_list);
             mr.Slot = vl->Slot;
             mr.InChanger = 1;
             if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
-                bsendmsg(ua, _("%s\n"), db_strerror(ua->db));
+                bsendmsg(ua, "%s", db_strerror(ua->db));
             } else {
                bsendmsg(ua, _(
                   "Catalog record for Volume \"%s\" updated to reference slot %d.\n"),
    if (ok) {
       sd = ua->jcr->store_bsock;
       if (relabel) {
+        /* Delete the old media record */
         if (!db_delete_media_record(ua->jcr, ua->db, &omr)) {
             bsendmsg(ua, _("Delete of Volume \"%s\" failed. ERR=%s"),
               omr.VolumeName, db_strerror(ua->db));
         } else {
             bsendmsg(ua, _("Old volume \"%s\" deleted from catalog.\n"), 
               omr.VolumeName);
+           /* Update the number of Volumes in the pool */
+           pr.NumVols--;
+           if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
+               bsendmsg(ua, "%s", db_strerror(ua->db));
+           }
         }
       }
       if (ua->automount) {
            if (db_create_media_record(ua->jcr, ua->db, &mr)) {
                bsendmsg(ua, _("Catalog record for cleaning tape \"%s\" successfully created.\n"),
                  mr.VolumeName);
+              pr.NumVols++;          /* this is a bit suspect */
+              if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
+                  bsendmsg(ua, "%s", db_strerror(ua->db));
+              }
            } else {
                bsendmsg(ua, "Catalog error on cleaning tape: %s", db_strerror(ua->db));
            }
 /*
  * NOTE! This routine opens the SD socket but leaves it open
  */
-static int send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr, 
-                             POOL_DBR *pr, int relabel, bool media_record_exists)
+static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr, 
+                              POOL_DBR *pr, int relabel, bool media_record_exists)
 {
    BSOCK *sd;
    char dev_name[MAX_NAME_LENGTH];
-   int ok = FALSE;
+   bool ok = false;
 
    if (!(sd=open_sd_bsock(ua))) {
-      return 0;
+      return false;
    }
    bstrncpy(dev_name, ua->jcr->store->dev_name, sizeof(dev_name));
    bash_spaces(dev_name);
    while (bnet_recv(sd) >= 0) {
       bsendmsg(ua, "%s", sd->msg);
       if (strncmp(sd->msg, "3000 OK label.", 14) == 0) {
-        ok = TRUE;
+        ok = true;
       } 
    }
    unbash_spaces(mr->VolumeName);
         mr->InChanger = 1;
         if (!db_update_media_record(ua->jcr, ua->db, mr)) {
              bsendmsg(ua, "%s", db_strerror(ua->db));
-            ok = FALSE;
+            ok = false;
         }
       } else {                       /* create the media record */
         set_pool_dbr_defaults_in_media_dbr(mr, pr);
         if (db_create_media_record(ua->jcr, ua->db, mr)) {
             bsendmsg(ua, _("Catalog record for Volume \"%s\", Slot %d  successfully created.\n"),
            mr->VolumeName, mr->Slot);
+           /* Update number of volumes in pool */
+           pr->NumVols++;
+           if (!db_update_pool_record(ua->jcr, ua->db, pr)) {
+               bsendmsg(ua, "%s", db_strerror(ua->db));
+           }
         } else {
             bsendmsg(ua, "%s", db_strerror(ua->db));
-           ok = FALSE;
+           ok = false;
         }
       }
    } else {
 
    int priority;
    bool hdr_printed = false;
    dlist sched;
-   sched_pkt *sp, *ip;
+   sched_pkt *sp;
 
    Dmsg0(200, "enter list_sched_jobs()\n");
 
         sp->priority = priority;
         sp->runtime = runtime;
         sp->pool = run->pool;
-        ip = (sched_pkt *)sched.binary_insert(sp, my_compare);
-        if (ip != sp) {
-           /* Identical entry already, we must explictly insert it */
-           sched.insert_after(sp, ip);
-        }
+        sched.binary_insert(sp, my_compare);
         num_jobs++;
       }
    } /* end for loop over resources */
 
 }
 
 /*
+ *  Insert an item in the list, but only if it is unique
+ *  otherwise, the item is returned non inserted
+ *
  * Returns: item        if item inserted
  *         other_item   if same value already exists (item not inserted)
  */
-void *dlist::binary_insert(void *item, int compare(void *item1, void *item2))
+void *dlist::unique_binary_insert(void *item, int compare(void *item1, void *item2))
 {
    int comp;
    int low, high, cur;
 }
 
 
+/*
+ *  Insert an item in the list, regardless if it is unique
+ *  or not.
+ */
+void dlist::binary_insert(void *item, int compare(void *item1, void *item2))
+{
+   void *ins_item = unique_binary_insert(item, compare);
+   /* If identical, insert after the one found */
+   if (ins_item != item) {
+      insert_after(item, ins_item);
+   }
+}
+
+
 void dlist::remove(void *item)
 {
    void *xitem;
 
    void append(void *item);
    void insert_before(void *item, void *where);
    void insert_after(void *item, void *where);
-   void *dlist::binary_insert(void *item, int compare(void *item1, void *item2));
+   void *unique_binary_insert(void *item, int compare(void *item1, void *item2));
+   void binary_insert(void *item, int compare(void *item1, void *item2));
    void remove(void *item);
    bool empty();
    int  size();
 
         scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"), 
           lc->str, lc->line_no, lc->line);
      }
+     if (*(item->value)) {
+        scan_err3(lc, _("Attempt to redefine resource \"%s\" referenced on line %d : %s\n"), 
+          lc->str, lc->line_no, lc->line);
+     }
      *(item->value) = (char *)res;
    }
    scan_to_eol(lc);
 
    TREE_NODE *node, *found_node;
    node = new_tree_node(root);
    node->fname = fname;
-   found_node = (TREE_NODE *)parent->child.binary_insert(node, node_compare);
+   found_node = (TREE_NODE *)parent->child.unique_binary_insert(node, node_compare);
    if (found_node != node) {         /* already in list */
       free_tree_node(root);          /* free node allocated above */
       found_node->inserted = false;
 
         break;
       }
       pm_strcpy(&jcr->VolumeName, newname);
-      bnet_fsend(dir, _("3000 OK label. Volume=%s Device=%s\n"), 
+      /* The following 3000 OK label. string is scanned in ua_label.c */
+      bnet_fsend(dir, "3000 OK label. Volume=%s Device=%s\n", 
         newname, dev_name(dev));
       break;
    case VOL_NO_MEDIA:
 bail_out:
    free_block(block);
    give_back_device_lock(dev, &hold);
-
    return;
 }
 
 
 {
    DEV_RECORD rec;   
    DEV_BLOCK *block;
-   bool stat = true;
+   bool ok = false;
 
 
    Dmsg0(99, "write_volume_label()\n");
    if (!write_record_to_block(block, &rec)) {
       Dmsg2(30, "Bad Label write on %s. ERR=%s\n", dev_name(dev), strerror_dev(dev));
       memset(&dev->VolHdr, 0, sizeof(dev->VolHdr));
-      free_block(block);
-      free_pool_memory(rec.data);
-      return false;
+      goto bail_out;
    } else {
       Dmsg2(30, "Wrote label of %d bytes to %s\n", rec.data_len, dev_name(dev));
    }
-   free_pool_memory(rec.data);
       
    Dmsg0(99, "Call write_block_to_dev()\n");
    if (!write_block_to_dev(jcr->dcr, block)) {
       memset(&dev->VolHdr, 0, sizeof(dev->VolHdr));
       Dmsg2(30, "Bad Label write on %s. ERR=%s\n", dev_name(dev), strerror_dev(dev));
-      stat = false;
+      goto bail_out;
    }
    Dmsg0(99, " Wrote block to device\n");
      
-   flush_dev(dev);
    weof_dev(dev, 1);
    dev->state |= ST_LABEL;
+   ok = true;
 
    if (debug_level >= 20)  {
       dump_volume_label(dev);
    }
+
+bail_out:
    free_block(block);
-   return stat;
+   free_pool_memory(rec.data);
+   return ok;
 }     
 
 
 
 #undef  VERSION
 #define VERSION "1.35.1"
 #define VSTRING "1"
-#define BDATE   "09 July 2004"
-#define LSMDATE "09Jul04"
+#define BDATE   "16 July 2004"
+#define LSMDATE "16Jul04"
 
 /* Debug flags */
 #undef  DEBUG
 
     zlib                The zlib library (third party)
     wx                  The wxWidgets library (third party)
 
+depkgs-win32 is released separate .tar.gz file that contains the
+source for the above mentioned libraries needed to build the
+Win32 Bacula.  It can be found in the Source Forge Bacula project
+release section.
+
 To build it:
 - Make sure nmake, and VC++ libraries and includes are setup correctly
   in PATH, INCLUDE, and LIB environment variables.