Kern's ToDo List
-                15 September 2003 
+                16 September 2003 
 
 Documentation to do: (any release a little bit at a time)
 - Document running a test version.
 - Test not zeroing Autochanger slot when it is wrong.
 - Figure out how to use ssh or stunnel to protect Bacula communications.
 
-For 1.32:
+For 1.32 Testing/Documentation:
 - Document list nextvol and new format status dir.
 - Document new records in Director. SDAddress SDDeviceName, SDPassword.
   FDPassword, FDAddress, DBAddress, DBPort, DBPassword.
-- It is generally safe to use the drive when the lights stop flashing.
+- Document that it is safe to use the drive when the lights stop flashing.
 - Document new Include/Exclude ...
 - Document all the status codes JobLevel, JobType, JobStatus.
 - Add test of exclusion, test multiple Include {} statements.
+                
+For 1.32:
 - Enhance "update slots" to include a "scan" feature
   scan 1; scan 1-5; scan 1,2,4 ...  to update the catalog 
 - Allow a slot or range of slots on the label barcodes command.
-  when the magazine is changed.
 - Don't print "Warning: Wrong Volume mounted ..." if mounting second volume.
-- LabelFormat on tape volume apparently creates the db record but 
-  never actually labels the volume.
-- Copy static programs into install directory.
+
+
+
 
 For 1.33
-- Think about changing Storage resource Device record to be
-  SDDeviceName.
+- Write a mini-readline with history and editing.
 - Look at how fuser works and /proc/PID/fd that is how Nic found the
   file descriptor leak in Bacula.
 - Implement WrapCounters in Counters.
 - Figure out some way to estimate output size and to avoid splitting
   a backup across two Volumes -- this could be useful for writing CDROMs
   where you really prefer not to have it split -- not serious.
-- Add RunBeforeJob and RunAfterJob to the Client program.
 - Have SD compute MD5 or SHA1 and compare to what FD computes.
 - Make VolumeToCatalog calculate an MD5 or SHA1 from the 
   actual data on the Volume and compare it.                  
 - Check if we can increase Bacula FD priorty in Win2000
 - Make sure the MaxVolFiles is fully implemented in SD
 - Check if both CatalogFiles and UseCatalog are set to SD.
-- Need return status on read_cb() from read_records(). Need multiple
-  records -- one per Job, maybe a JCR or some other structure with
-  a block and a record.
 - Figure out how to do a bare metal Windows restore
 - Possibly add email to Watchdog if drive is unmounted too
   long and a job is waiting on the drive.
 - Document restore by files.
 - Make variable expansion work correctly.
 - Implement List Volume Job=xxx  or List scheduled volumes or Status Director 
+- Copy static programs into install directory.
+- Think about changing Storage resource Device record to be
+  SDDeviceName.
+- Add RunBeforeJob and RunAfterJob to the Client program.
+- Need return status on read_cb() from read_records(). Need multiple
+  records -- one per Job, maybe a JCR or some other structure with
+  a block and a record.
+- LabelFormat on tape volume apparently creates the db record but 
+  never actually labels the volume.
+
 
 #define mp_chr(x) ((char*)(x))
 class POOLMEM { 
 public:
-   POOLMEM() { }
+   POOLMEM();
    operator char * const() { return this; }
 };
 #endif
 
 db_create_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr)
 {
    int stat;
-   char ed1[30], ed2[30], ed3[30], ed4[30];
+   char ed1[30], ed2[30], ed3[30], ed4[30], ed5[30];
    char dt[MAX_TIME_LENGTH];
    struct tm tm;
 
       localtime_r(&mr->LabelDate, &tm); 
       strftime(dt, sizeof(dt), "%Y-%m-%d %T", &tm);
    } else {
-      strcpy(dt, "0000-00-00 00:00:00");
+      bstrncpy(dt, "0000-00-00 00:00:00", sizeof(dt));
    }
    Mmsg(&mdb->cmd, 
 "INSERT INTO Media (VolumeName,MediaType,PoolId,MaxVolBytes,VolCapacityBytes," 
 "Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
-"VolStatus,LabelDate,Slot) "
-"VALUES ('%s','%s',%u,%s,%s,%d,%s,%s,%u,%u,'%s','%s',%d)", 
+"VolStatus,LabelDate,Slot,VolBytes) "
+"VALUES ('%s','%s',%u,%s,%s,%d,%s,%s,%u,%u,'%s','%s',%d,%s)", 
                  mr->VolumeName,
                  mr->MediaType, mr->PoolId, 
                  edit_uint64(mr->MaxVolBytes,ed1),
                  mr->MaxVolJobs,
                  mr->MaxVolFiles,
                  mr->VolStatus, dt,
-                 mr->Slot);
+                 mr->Slot,
+                 edit_uint64(mr->VolBytes, ed5));
 
    Dmsg1(500, "Create Volume: %s\n", mdb->cmd);
    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
 
       Dmsg1(400, "Firstwritten stat=%d\n", stat);
    }
 
+   /* Label just done? */
+   if (mr->VolBytes == 1) {
+      ttime = mr->LabelDate;
+      if (ttime == 0) {
+        ttime = time(NULL);
+      }
+      localtime_r(&ttime, &tm);
+      strftime(dt, sizeof(dt), "%Y-%m-%d %T", &tm);
+      Mmsg(&mdb->cmd, "UPDATE Media SET LabelDate='%s'\
+ WHERE VolumeName='%s'", dt, mr->VolumeName);
+      stat = UPDATE_DB(jcr, mdb, mdb->cmd);
+   }
+
    ttime = mr->LastWritten;
    localtime_r(&ttime, &tm);
    strftime(dt, sizeof(dt), "%Y-%m-%d %T", &tm);
 
           $(MV) -f ${DESTDIR}${scriptdir}/query.sql ${DESTDIR}${scriptdir}/query.sql.save; \
        fi 
        ${INSTALL_DATA} query.sql ${DESTDIR}${scriptdir}/query.sql
+       @if test -f static-bacula-dir; then \
+          $(INSTALL_PROGRAM) static-bacula-dir $(DESTDIR)$(sbindir)/static-bacula-dir; \
+       fi
 
 
 uninstall:
 
    MEDIA_DBR mr, sdmr; 
    JOBMEDIA_DBR jm;
    char Job[MAX_NAME_LENGTH];
-   int index, ok, relabel, writing;
+   int index, ok, label, writing;
    POOLMEM *omsg;
 
    memset(&mr, 0, sizeof(mr));
    } else if (sscanf(bs->msg, Update_media, &Job, &sdmr.VolumeName, &sdmr.VolJobs,
       &sdmr.VolFiles, &sdmr.VolBlocks, &sdmr.VolBytes, &sdmr.VolMounts, &sdmr.VolErrors,
       &sdmr.VolWrites, &sdmr.MaxVolBytes, &sdmr.LastWritten, &sdmr.VolStatus, 
-      &sdmr.Slot, &relabel) == 14) {
+      &sdmr.Slot, &label) == 14) {
 
       db_lock(jcr->db);
       Dmsg3(400, "Update media %s oldStat=%s newStat=%s\n", sdmr.VolumeName,
       if (mr.VolJobs == 0 || sdmr.VolJobs == 1) {
         mr.FirstWritten = jcr->start_time;   /* use Job start time as first write */
       }
+      /* If we just labeled the tape set time */
+      Dmsg2(400, "label=%d labeldate=%d\n", label, mr.LabelDate);
+      if (label || mr.LabelDate == 0) {
+        mr.LabelDate = time(NULL);
+      }
       Dmsg2(200, "Update media: BefVolJobs=%u After=%u\n", mr.VolJobs, sdmr.VolJobs);
       /* Copy updated values to original media record */
       mr.VolJobs     = sdmr.VolJobs;
        * Update Media Record
        */
 
-      /* Check limits and expirations if "Append" and not a relable request */
-      if (strcmp(mr.VolStatus, "Append") == 0 && !relabel) {
+      /* Check limits and expirations if "Append" and not a lable request */
+      if (strcmp(mr.VolStatus, "Append") == 0 && !label) {
         /* First handle Max Volume Bytes */
         if ((mr.MaxVolBytes > 0 && mr.VolBytes >= mr.MaxVolBytes)) {
             Jmsg(jcr, M_INFO, 0, _("Max Volume bytes exceeded. "             
 
 
 
 /*
- * Automatic Volume name creation using
- *  LabelFormat. We assume that if this routine is being
- *  called the Volume will be labeled, so we set the LabelDate.
+ * Automatic Volume name creation using the LabelFormat
  */
 int newVolume(JCR *jcr, MEDIA_DBR *mr)
 {
       if (pr.MaxVols == 0 || pr.NumVols < pr.MaxVols) {
         memset(mr, 0, sizeof(MEDIA_DBR));
         set_pool_dbr_defaults_in_media_dbr(mr, &pr);
-        mr->LabelDate = time(NULL);
         bstrncpy(mr->MediaType, jcr->store->media_type, sizeof(mr->MediaType));
         /* Check for special characters */
         if (is_volume_name_legal(NULL, pr.LabelFormat)) {
 
 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
 {
    mr->PoolId = pr->PoolId;
-   strcpy(mr->VolStatus, "Append");
+   bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus));
    mr->Recycle = pr->Recycle;
    mr->VolRetention = pr->VolRetention;
    mr->VolUseDuration = pr->VolUseDuration;
 
         }
       }
       if (ua->automount) {
-        strcpy(dev_name, store->dev_name);
+        bstrncpy(dev_name, store->dev_name, sizeof(dev_name));
          bsendmsg(ua, _("Requesting mount %s ...\n"), dev_name);
         bash_spaces(dev_name);
          bnet_fsend(sd, "mount %s", dev_name);
    bash_spaces(pr->Name);
    if (relabel) {
       bash_spaces(omr->VolumeName);
-      bnet_fsend(sd, _("relabel %s OldName=%s NewName=%s PoolName=%s MediaType=%s Slot=%d"), 
+      bnet_fsend(sd, "relabel %s OldName=%s NewName=%s PoolName=%s MediaType=%s Slot=%d", 
         dev_name, omr->VolumeName, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot);
       bsendmsg(ua, _("Sending relabel command from \"%s\" to \"%s\" ...\n"),
         omr->VolumeName, mr->VolumeName);
    } else {
-      bnet_fsend(sd, _("label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d"), 
+      bnet_fsend(sd, "label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d", 
         dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot);
       bsendmsg(ua, _("Sending label command for Volume \"%s\" Slot %d ...\n"), 
         mr->VolumeName, mr->Slot);
    mr->LabelDate = time(NULL);
    if (ok) {
       set_pool_dbr_defaults_in_media_dbr(mr, pr);
+      mr->VolBytes = 1;              /* flag indicating Volume labeled */
       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);
 
        fi; \
        echo "${INSTALL_CONFIG} $$srcconf ${DESTDIR}${sysconfdir}/$$destconf"; \
        ${INSTALL_CONFIG} $$srcconf ${DESTDIR}${sysconfdir}/$$destconf
+       @if test -f static-bacula-fd; then \
+          $(INSTALL_PROGRAM) static-bacula-fd $(DESTDIR)$(sbindir)/static-bacula-fd; \
+       fi
+
+
 
 uninstall:
        (cd $(DESTDIR)$(sbindir); $(RMF) bacula-fd)
 
        fi; \
        echo "${INSTALL_CONFIG} $$srcconf ${DESTDIR}${sysconfdir}/$$destconf"; \
        ${INSTALL_CONFIG} $$srcconf ${DESTDIR}${sysconfdir}/$$destconf
+       @if test -f static-bacula-sd; then \
+          $(INSTALL_PROGRAM) static-bacula-sd $(DESTDIR)$(sbindir)/static-bacula-sd; \
+       fi
 
 uninstall:
        (cd $(DESTDIR)$(sbindir); $(RMF) bacula-sd)
 
  * After writing a Volume, send the updated statistics
  * back to the director.
  */
-int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel)
+int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int label)
 {
    BSOCK *dir = jcr->dir_bsock;
    time_t EndTime = time(NULL);
       Jmsg0(jcr, M_ERROR, 0, _("NULL Volume name. This shouldn't happen!!!\n"));
       return 0;
    }
+   /* Just labeled or relabeled the tape */
+   if (label) {
+      bstrncpy(vol->VolCatStatus, "Append", sizeof(vol->VolCatStatus));
+      vol->VolCatBytes = 1;          /* indicates tape labeled */
+   }
    bash_spaces(vol->VolCatName);
    bnet_fsend(dir, Update_media, jcr->Job, 
       vol->VolCatName, vol->VolCatJobs, vol->VolCatFiles,
       vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
       vol->VolCatMounts, vol->VolCatErrors,
       vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2), 
-      EndTime, vol->VolCatStatus, vol->Slot, relabel);
+      EndTime, vol->VolCatStatus, vol->Slot, label);
    Dmsg1(120, "update_volume_data(): %s", dir->msg);
    unbash_spaces(vol->VolCatName);
    if (bnet_recv(dir) <= 0) {
 
    return dev->fd;
 }
 
+#ifdef debug_tracing
+#undef rewind_dev
+int _rewind_dev(char *file, int line, DEVICE *dev)
+{
+   Dmsg2(000, "rewind_dev called from %s:%d\n", file, line);
+   return rewind_dev(dev);
+}
+#endif
+
+
 /*
  * Rewind the device.
  *  Returns: 1 on success
    struct mtop mt_com;
    unsigned int i;
 
-   Dmsg0(29, "rewind_dev\n");
+   Dmsg1(29, "rewind_dev %s\n", dev->dev_name);
    if (dev->fd < 0) {
       dev->dev_errno = EBADF;
       Mmsg1(&dev->errmsg, _("Bad call to rewind_dev. Device %s not open\n"),
 
    /* ***FIXME*** we really need to get the volume name,    
     * pool name, and pool type from the database.
     */
-   strcpy(dev->VolHdr.Id, BaculaId);
+   bstrncpy(dev->VolHdr.Id, BaculaId, sizeof(dev->VolHdr.Id));
    dev->VolHdr.VerNum = BaculaTapeVersion;
    dev->VolHdr.LabelType = PRE_LABEL;  /* Mark tape as unused */
    bstrncpy(dev->VolHdr.VolName, VolName, sizeof(dev->VolHdr.VolName));
    bstrncpy(dev->VolHdr.PoolName, PoolName, sizeof(dev->VolHdr.PoolName));
    bstrncpy(dev->VolHdr.MediaType, device->media_type, sizeof(dev->VolHdr.MediaType));
 
-   strcpy(dev->VolHdr.PoolType, "Backup");
+   bstrncpy(dev->VolHdr.PoolType, "Backup", sizeof(dev->VolHdr.PoolType));
 
    /* Put label time/date in header */
    if (BaculaTapeVersion >= 11) {
 
    for ( ;; ) {
       int vol_label_status;
       autochanger = autoload_device(jcr, dev, 1, NULL);
+      Dmsg1(100, "autoload_dev returns %d\n", autochanger);
 
       /*
        * If we autochanged to correct Volume or (we have not just
            goto mount_error;
         }
          Dmsg1(100, "want new name=%s\n", jcr->VolumeName);
-        memcpy(&dev->VolCatInfo, &jcr->VolCatInfo, sizeof(jcr->VolCatInfo));
+        memcpy(&dev->VolCatInfo, &jcr->VolCatInfo, sizeof(dev->VolCatInfo));
          if (strcmp(dev->VolCatInfo.VolCatStatus, "Recycle") == 0) {
            recycle = 1;
         }
        */
       case VOL_NO_LABEL:
       case VOL_IO_ERROR:
-         Dmsg1(500, "Vol NO_LABEL or IO_ERROR name=%s\n", jcr->VolumeName);
-        /* If permitted, create a label */
-        if (dev_cap(dev, CAP_LABEL)) {
+        /* 
+         * If permitted, we label the device, make sure we can do
+         *   it by checking that the VolCatBytes is zero => not labeled. 
+         */
+        if (dev_cap(dev, CAP_LABEL) && dev->VolCatInfo.VolCatBytes == 0) {
             Dmsg0(100, "Create volume label\n");
-           /* ***FIXME*** ask for label name */
            if (!write_volume_label_to_dev(jcr, (DEVRES *)dev->device, jcr->VolumeName,
                   jcr->pool_name)) {
                Dmsg0(100, "!write_vol_label\n");
               goto mount_next_vol;
            }
+            Dmsg0(200, "dir_update_vol_info. Set Append\n");
+           memcpy(&dev->VolCatInfo, &jcr->VolCatInfo, sizeof(dev->VolCatInfo));
+           dir_update_volume_info(jcr, &dev->VolCatInfo, 1);  /* indicate tape labeled */
             Jmsg(jcr, M_INFO, 0, _("Labeled new Volume \"%s\" on device %s.\n"),
               jcr->VolumeName, dev_name(dev));
            goto read_volume;      /* read label we just wrote */
        *  It is better to find out now rather than later.
        */
       if (!dev_cap(dev, CAP_STREAM)) {
-        dev->VolCatInfo.VolCatBytes = 0;
         if (!rewind_dev(dev)) {
             Jmsg2(jcr, M_WARNING, 0, _("Rewind error on device %s. ERR=%s\n"), 
                  dev_name(dev), strerror_dev(dev));
       /* Set or reset Volume statistics */
       dev->VolCatInfo.VolCatJobs = 0;
       dev->VolCatInfo.VolCatFiles = 0;
+      dev->VolCatInfo.VolCatBytes = 0;
       dev->VolCatInfo.VolCatErrors = 0;
       dev->VolCatInfo.VolCatBlocks = 0;
       dev->VolCatInfo.VolCatRBytes = 0;
         dev->VolCatInfo.VolCatWrites = 1;
         dev->VolCatInfo.VolCatReads = 1;
       }
-      bstrncpy(dev->VolCatInfo.VolCatStatus, "Append", sizeof(dev->VolCatInfo.VolCatStatus));
       Dmsg0(200, "dir_update_vol_info. Set Append\n");
       dir_update_volume_info(jcr, &dev->VolCatInfo, 1);  /* indicate doing relabel */
       if (recycle) {
 
 };
 int     dir_get_volume_info(JCR *jcr, enum get_vol_info_rw);
 int     dir_find_next_appendable_volume(JCR *jcr);
-int     dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel);
+int     dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int label);
 int     dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev);
 int     dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev);
 int     dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec);
 
 
 extern STORES *me;                    /* "Global" daemon resource */
 
+#ifdef debug_tracing
+extern int _rewind_dev(char *file, int line, DEVICE *dev);
+#define rewind_dev(d) _rewind_dev(__FILE__, __LINE__, (d))
+#endif
+
 #endif /* __STORED_H_ */