Esempio n. 1
0
/*
 * Common routine for both label and relabel
 */
static int do_label(UAContext *ua, const char *cmd, bool relabel)
{
   USTORERES store;
   BSOCK *sd;
   char dev_name[MAX_NAME_LENGTH];
   MEDIA_DBR mr, omr;
   POOL_DBR pr;
   bool print_reminder = true;
   bool label_barcodes = false;
   bool label_encrypt = false;
   int ok = FALSE;
   int i, j;
   int drive;
   bool media_record_exists = false;
   static const char *barcode_keywords[] = {
      "barcode",
      "barcodes",
      NULL
   };

   memset(&pr, 0, sizeof(pr));
   if (!open_client_db(ua)) {
      return 1;
   }

   /*
    * Look for one of the barcode keywords
    */
   if (!relabel && (i = find_arg_keyword(ua, barcode_keywords)) >= 0) {
      /*
       * Now find the keyword in the list
       */
      if ((j = find_arg(ua, barcode_keywords[i])) > 0) {
         *ua->argk[j] = 0;      /* zap barcode keyword */
      }
      label_barcodes = true;
   }

   /*
    * Look for the encrypt keyword
    */
   if ((i = find_arg(ua, "encrypt")) > 0) {
      *ua->argk[i] = 0;         /* zap encrypt keyword */
      label_encrypt = true;
   }

   store.store = get_storage_resource(ua, true, label_barcodes);
   if (!store.store) {
      return 1;
   }

   switch (store.store->Protocol) {
   case APT_NDMPV2:
   case APT_NDMPV3:
   case APT_NDMPV4:
      /*
       * See if the user selected a NDMP storage device but its
       * handled by a native Bareos storage daemon e.g. we have
       * a paired_storage pointer.
       */
      if (store.store->paired_storage) {
         store.store = store.store->paired_storage;
      } else {
         ua->warning_msg(_("Storage has non-native protocol.\n"));
         return 1;
      }
      break;
   default:
      break;
   }

   pm_strcpy(store.store_source, _("command line"));
   set_wstorage(ua->jcr, &store);
   drive = get_storage_drive(ua, store.store);

   if (label_barcodes) {
      label_from_barcodes(ua, drive, label_encrypt);
      return 1;
   }

   /*
    * If relabel get name of Volume to relabel
    */
   if (relabel) {
      /*
       * Check for oldvolume=name
       */
      i = find_arg_with_value(ua, "oldvolume");
      if (i >= 0) {
         bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
         if (db_get_media_record(ua->jcr, ua->db, &omr)) {
            goto checkVol;
         }
         ua->error_msg("%s", db_strerror(ua->db));
      }
      /*
       * No keyword or Vol not found, ask user to select
       */
      if (!select_media_dbr(ua, &omr)) {
         return 1;
      }

      /*
       * Require Volume to be Purged or Recycled
       */
checkVol:
      if (!bstrcmp(omr.VolStatus, "Purged") && !bstrcmp(omr.VolStatus, "Recycle")) {
         ua->error_msg(_("Volume \"%s\" has VolStatus %s. It must be Purged or Recycled before relabeling.\n"),
            omr.VolumeName, omr.VolStatus);
         return 1;
      }
   }

   /*
    * Check for volume=NewVolume
    */
   i = find_arg_with_value(ua, "volume");
   if (i >= 0) {
      pm_strcpy(ua->cmd, ua->argv[i]);
      goto checkName;
   }

   /*
    * Get a new Volume name
    */
   for ( ;; ) {
      media_record_exists = false;
      if (!get_cmd(ua, _("Enter new Volume name: "))) {
         return 1;
      }
checkName:
      if (!is_volume_name_legal(ua, ua->cmd)) {
         continue;
      }

      /*
       * Search by Media name so set VolumeName and clear MediaId.
       */
      mr.MediaId = 0;
      bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName));

      /*
       * If VolBytes are zero the Volume is not labeled
       */
      if (db_get_media_record(ua->jcr, ua->db, &mr)) {
         if (mr.VolBytes != 0) {
             ua->error_msg(_("Media record for new Volume \"%s\" already exists.\n"),
                mr.VolumeName);
             continue;
          }
          media_record_exists = true;
      }
      break;                          /* Got it */
   }

   /*
    * If autochanger, request slot
    */
   i = find_arg_with_value(ua, "slot");
   if (i >= 0) {
      mr.Slot = atoi(ua->argv[i]);
      if (mr.Slot < 0) {
         mr.Slot = 0;
      }
      mr.InChanger = mr.Slot > 0;  /* if slot give assume in changer */
   } else if (store.store->autochanger) {
      if (!get_pint(ua, _("Enter slot (0 or Enter for none): "))) {
         return 1;
      }
      mr.Slot = ua->pint32_val;
      if (mr.Slot < 0) {
         mr.Slot = 0;
      }
      mr.InChanger = mr.Slot > 0;  /* if slot give assume in changer */
   }
   set_storageid_in_mr(store.store, &mr);

   bstrncpy(mr.MediaType, store.store->media_type, sizeof(mr.MediaType));

   /*
    * Must select Pool if not already done
    */
   if (pr.PoolId == 0) {
      memset(&pr, 0, sizeof(pr));
      if (!select_pool_dbr(ua, &pr)) {
         return 1;
      }
   }

   /*
    * See if we need to generate a new passphrase for hardware encryption.
    */
   if (label_encrypt) {
      ua->info_msg(_("Generating new hardware encryption key\n"));
      if (!generate_new_encryption_key(ua, &mr)) {
         return 1;
      }
   }

   ok = send_label_request(ua, &mr, &omr, &pr, relabel, media_record_exists, drive);

   if (ok) {
      sd = ua->jcr->store_bsock;
      if (relabel) {
         /*
          * Delete the old media record
          */
         if (!db_delete_media_record(ua->jcr, ua->db, &omr)) {
            ua->error_msg(_("Delete of Volume \"%s\" failed. ERR=%s"),
               omr.VolumeName, db_strerror(ua->db));
         } else {
            ua->info_msg(_("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)) {
               ua->error_msg("%s", db_strerror(ua->db));
            }
         }
      }
      if (ua->automount) {
         bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
         ua->info_msg(_("Requesting to mount %s ...\n"), dev_name);
         bash_spaces(dev_name);
         sd->fsend("mount %s drive=%d", dev_name, drive);
         unbash_spaces(dev_name);

         /*
          * We use bget_dirmsg here and not bnet_recv because as part of
          * the mount request the stored can request catalog information for
          * any plugin who listens to the bsdEventLabelVerified event.
          * As we don't want to loose any non protocol data e.g. errors
          * without a 3xxx prefix we set the allow_any_message of
          * bget_dirmsg to true and as such is behaves like a normal
          * bnet_recv for any non protocol messages.
          */
         while (bget_dirmsg(sd, true) >= 0) {
            ua->send_msg("%s", sd->msg);

            /*
             * Here we can get
             *  3001 OK mount. Device=xxx      or
             *  3001 Mounted Volume vvvv
             *  3002 Device "DVD-Writer" (/dev/hdc) is mounted.
             *  3906 is cannot mount non-tape
             * So for those, no need to print a reminder
             */
            if (bstrncmp(sd->msg, "3001 ", 5) ||
                bstrncmp(sd->msg, "3002 ", 5) ||
                bstrncmp(sd->msg, "3906 ", 5)) {
               print_reminder = false;
            }
         }
      }
   }

   if (print_reminder) {
      ua->info_msg(_("Do not forget to mount the drive!!!\n"));
   }

   close_sd_bsock(ua);

   return 1;
}
Esempio n. 2
0
/*
 * Common routine for both label and relabel
 */
static int do_label(UAContext *ua, const char *cmd, int relabel)
{
   USTORE store;
   BSOCK *sd;
   char dev_name[MAX_NAME_LENGTH];
   MEDIA_DBR mr, omr;
   POOL_DBR pr;
   bool print_reminder = true;
   bool label_barcodes = false;
   int ok = FALSE;
   int i, j;
   int drive;
   bool media_record_exists = false;
   static const char *barcode_keyword[] = {
      "barcode",
      "barcodes",
      NULL};


   memset(&pr, 0, sizeof(pr));
   if (!open_client_db(ua)) {
      return 1;
   }

   /* Look for one of the barcode keywords */
   if (!relabel && (i=find_arg_keyword(ua, barcode_keyword)) >= 0) {
      /* Now find the keyword in the list */
      if ((j = find_arg(ua, barcode_keyword[i])) > 0) {
         *ua->argk[j] = 0;      /* zap barcode keyword */
      }
      label_barcodes = true;
   }

   store.store = get_storage_resource(ua, true/*use default*/);
   if (!store.store) {
      return 1;
   }
   pm_strcpy(store.store_source, _("command line"));
   set_wstorage(ua->jcr, &store);
   drive = get_storage_drive(ua, store.store);

   if (label_barcodes) {
      label_from_barcodes(ua, drive);
      return 1;
   }

   /* If relabel get name of Volume to relabel */
   if (relabel) {
      /* Check for oldvolume=name */
      i = find_arg_with_value(ua, "oldvolume");
      if (i >= 0) {
         memset(&omr, 0, sizeof(omr));
         bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
         if (db_get_media_record(ua->jcr, ua->db, &omr)) {
            goto checkVol;
         }
         ua->error_msg("%s", db_strerror(ua->db));
      }
      /* No keyword or Vol not found, ask user to select */
      if (!select_media_dbr(ua, &omr)) {
         return 1;
      }

      /* Require Volume to be Purged or Recycled */
checkVol:
      if (strcmp(omr.VolStatus, "Purged") != 0 && strcmp(omr.VolStatus, "Recycle") != 0) {
         ua->error_msg(_("Volume \"%s\" has VolStatus %s. It must be Purged or Recycled before relabeling.\n"),
            omr.VolumeName, omr.VolStatus);
         return 1;
      }
   }

   /* Check for volume=NewVolume */
   i = find_arg_with_value(ua, "volume");
   if (i >= 0) {
      pm_strcpy(ua->cmd, ua->argv[i]);
      goto checkName;
   }

   /* Get a new Volume name */
   for ( ;; ) {
      media_record_exists = false;
      if (!get_cmd(ua, _("Enter new Volume name: "))) {
         return 1;
      }
checkName:
      if (!is_volume_name_legal(ua, ua->cmd)) {
         continue;
      }

      memset(&mr, 0, sizeof(mr));
      bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName));
      /* If VolBytes are zero the Volume is not labeled */
      if (db_get_media_record(ua->jcr, ua->db, &mr)) {
         if (mr.VolBytes != 0) {
             ua->error_msg(_("Media record for new Volume \"%s\" already exists.\n"),
                mr.VolumeName);
             continue;
          }
          media_record_exists = true;
      }
      break;                          /* Got it */
   }

   /* If autochanger, request slot */
   i = find_arg_with_value(ua, "slot");
   if (i >= 0) {
      mr.Slot = atoi(ua->argv[i]);
      if (mr.Slot < 0) {
         mr.Slot = 0;
      }
      mr.InChanger = mr.Slot > 0;  /* if slot give assume in changer */
   } else if (store.store->autochanger) {
      if (!get_pint(ua, _("Enter slot (0 or Enter for none): "))) {
         return 1;
      }
      mr.Slot = ua->pint32_val;
      if (mr.Slot < 0) {
         mr.Slot = 0;
      }
      mr.InChanger = mr.Slot > 0;  /* if slot give assume in changer */
   }
   mr.StorageId = store.store->StorageId;

   bstrncpy(mr.MediaType, store.store->media_type, sizeof(mr.MediaType));

   /* Must select Pool if not already done */
   if (pr.PoolId == 0) {
      memset(&pr, 0, sizeof(pr));
      if (!select_pool_dbr(ua, &pr)) {
         return 1;
      }
   }

   ok = send_label_request(ua, &mr, &omr, &pr, relabel, media_record_exists, drive);

   if (ok) {
      sd = ua->jcr->store_bsock;
      if (relabel) {
         /* Delete the old media record */
         if (!db_delete_media_record(ua->jcr, ua->db, &omr)) {
            ua->error_msg(_("Delete of Volume \"%s\" failed. ERR=%s"),
               omr.VolumeName, db_strerror(ua->db));
         } else {
            ua->info_msg(_("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)) {
               ua->error_msg("%s", db_strerror(ua->db));
            }
         }
      }
      if (ua->automount) {
         bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
         ua->info_msg(_("Requesting to mount %s ...\n"), dev_name);
         bash_spaces(dev_name);
         bnet_fsend(sd, "mount %s drive=%d", dev_name, drive);
         unbash_spaces(dev_name);
         while (bnet_recv(sd) >= 0) {
            ua->send_msg("%s", sd->msg);
            /* Here we can get
             *  3001 OK mount. Device=xxx      or
             *  3001 Mounted Volume vvvv
             *  3002 Device "DVD-Writer" (/dev/hdc) is mounted.
             *  3906 is cannot mount non-tape
             * So for those, no need to print a reminder
             */
            if (strncmp(sd->msg, "3001 ", 5) == 0 ||
                strncmp(sd->msg, "3002 ", 5) == 0 ||
                strncmp(sd->msg, "3906 ", 5) == 0) {
               print_reminder = false;
            }
         }
      }
   }
   if (print_reminder) {
      ua->info_msg(_("Do not forget to mount the drive!!!\n"));
   }
   close_sd_bsock(ua);

   return 1;
}
Esempio n. 3
0
/*
 * Print slots from AutoChanger
 */
void status_slots(UAContext *ua, STORE *store_r)
{
   USTORE store;
   POOL_DBR pr;
   vol_list_t *vl, *vol_list = NULL;
   MEDIA_DBR mr;
   char *slot_list;
   int max_slots;
   int drive;
   int i=1;
   /* output format */
   const char *slot_api_empty_format="%i|||||\n";
   const char *slot_api_full_format="%i|%i|%s|%s|%s|%s|\n";
   const char *slot_hformat=" %4i%c| %16s | %9s | %20s | %18s |\n";

   if (!open_client_db(ua)) {
      return;
   }
   store.store = store_r;

   pm_strcpy(store.store_source, _("command line"));
   set_wstorage(ua->jcr, &store);
   drive = get_storage_drive(ua, store.store);

   max_slots = get_num_slots_from_SD(ua);

   if (max_slots <= 0) {
      ua->warning_msg(_("No slots in changer to scan.\n"));
      return;
   }
   slot_list = (char *)malloc(max_slots+1);
   if (!get_user_slot_list(ua, slot_list, max_slots)) {
      free(slot_list);
      return;
   }

   vol_list = get_vol_list_from_SD(ua, true /* want to see all slots */);

   if (!vol_list) {
      ua->warning_msg(_("No Volumes found, or no barcodes.\n"));
      goto bail_out;
   }
   if (!ua->api) {
      ua->info_msg(_(" Slot |   Volume Name    |   Status  |     Media Type       |      Pool          |\n"));
      ua->info_msg(_("------+------------------+-----------+----------------------+--------------------|\n"));
   }

   /* Walk through the list getting the media records */
   for (vl=vol_list; vl; vl=vl->next) {
      if (vl->Slot > max_slots) {
         ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
            vl->Slot, max_slots);
         continue;
      }
      /* Check if user wants us to look at this slot */
      if (!slot_list[vl->Slot]) {
         Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
         continue;
      }

      slot_list[vl->Slot] = 0;        /* clear Slot */

      if (!vl->VolName) {
         Dmsg1(100, "No VolName for Slot=%d.\n", vl->Slot);
         if (!ua->api) {
            ua->info_msg(slot_hformat,
                         vl->Slot, '*',
                         "?", "?", "?", "?");
         } else {
            ua->info_msg(slot_api_empty_format, vl->Slot);
         }
         continue;
      }

      /* Hope that slots are ordered */
      for (; i < vl->Slot; i++) {
         if (slot_list[i]) {
            if (!ua->api) {
               ua->info_msg(slot_hformat,
                            i, ' ', "", "", "", "");
            } else {
               ua->info_msg(slot_api_empty_format, i);
            }       
            slot_list[i]=0;
         }
      }

      memset(&mr, 0, sizeof(mr));
      bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
      db_lock(ua->db);
      if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) {
         memset(&pr, 0, sizeof(POOL_DBR));
         pr.PoolId = mr.PoolId;
         if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
            strcpy(pr.Name, "?");
         }

         if (!ua->api) {
            /* Print information */
            ua->info_msg(slot_hformat,
                         vl->Slot, ((vl->Slot==mr.Slot)?' ':'*'),
                         mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name);
         } else {
            ua->info_msg(slot_api_full_format,
                         vl->Slot, mr.Slot, mr.VolumeName, mr.VolStatus, 
                         mr.MediaType, pr.Name);
         }

         db_unlock(ua->db);
         continue;
      } else {                  /* TODO: get information from catalog  */
         ua->info_msg(slot_hformat,
                      vl->Slot, '*',
                      mr.VolumeName, "?", "?", "?");
      }
      db_unlock(ua->db);
   }

   /* Display the rest of the autochanger
    */
   for (; i <= max_slots; i++) {
      if (slot_list[i]) {
         if (!ua->api) {
            ua->info_msg(slot_hformat,
                         i, ' ', "", "", "", "");
         } else {
            ua->info_msg(slot_api_empty_format, i);
         } 
         slot_list[i]=0;
      }
   }

bail_out:

   free_vol_list(vol_list);
   free(slot_list);
   close_sd_bsock(ua);

   return;
}
Esempio n. 4
0
/*
 * Update Slots corresponding to Volumes in autochanger
 */
void update_slots(UAContext *ua)
{
   USTORE store;
   vol_list_t *vl, *vol_list = NULL;
   MEDIA_DBR mr;
   char *slot_list;
   bool scan;
   int max_slots;
   int drive;
   int Enabled = 1;
   bool have_enabled;
   int i;


   if (!open_client_db(ua)) {
      return;
   }
   store.store = get_storage_resource(ua, true/*arg is storage*/);
   if (!store.store) {
      return;
   }
   pm_strcpy(store.store_source, _("command line"));
   set_wstorage(ua->jcr, &store);
   drive = get_storage_drive(ua, store.store);

   scan = find_arg(ua, NT_("scan")) >= 0;
   if ((i=find_arg_with_value(ua, NT_("Enabled"))) >= 0) {
      Enabled = get_enabled(ua, ua->argv[i]);
      if (Enabled < 0) {
         return;
      }
      have_enabled = true;
   } else {
      have_enabled = false;
   }

   max_slots = get_num_slots_from_SD(ua);
   Dmsg1(100, "max_slots=%d\n", max_slots);
   if (max_slots <= 0) {
      ua->warning_msg(_("No slots in changer to scan.\n"));
      return;
   }
   slot_list = (char *)malloc(max_slots+1);
   if (!get_user_slot_list(ua, slot_list, max_slots)) {
      free(slot_list);
      return;
   }

   vol_list = get_vol_list_from_SD(ua, scan);

   if (!vol_list) {
      ua->warning_msg(_("No Volumes found to label, or no barcodes.\n"));
      goto bail_out;
   }

   /* First zap out any InChanger with StorageId=0 */
   db_sql_query(ua->db, "UPDATE Media SET InChanger=0 WHERE StorageId=0", NULL, NULL);

   /* Walk through the list updating the media records */
   for (vl=vol_list; vl; vl=vl->next) {
      if (vl->Slot > max_slots) {
         ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
            vl->Slot, max_slots);
         continue;
      }
      /* Check if user wants us to look at this slot */
      if (!slot_list[vl->Slot]) {
         Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
         continue;
      }
      /* If scanning, we read the label rather than the barcode */
      if (scan) {
         if (vl->VolName) {
            free(vl->VolName);
            vl->VolName = NULL;
         }
         vl->VolName = get_volume_name_from_SD(ua, vl->Slot, drive);
         Dmsg2(100, "Got Vol=%s from SD for Slot=%d\n", vl->VolName, vl->Slot);
      }
      slot_list[vl->Slot] = 0;        /* clear Slot */
      memset(&mr, 0, sizeof(mr));
      mr.Slot = vl->Slot;
      mr.InChanger = 1;
      mr.StorageId = store.store->StorageId;
      /* Set InChanger to zero for this Slot */
      db_lock(ua->db);
      db_make_inchanger_unique(ua->jcr, ua->db, &mr);
      db_unlock(ua->db);
      if (!vl->VolName) {
         Dmsg1(100, "No VolName for Slot=%d setting InChanger to zero.\n", vl->Slot);
         ua->info_msg(_("No VolName for Slot=%d InChanger set to zero.\n"), vl->Slot);
         continue;
      }
      memset(&mr, 0, sizeof(mr));
      bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
      db_lock(ua->db);
      if (db_get_media_record(ua->jcr, ua->db, &mr)) {
         if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != store.store->StorageId) {
            mr.Slot = vl->Slot;
            mr.InChanger = 1;
            mr.StorageId = store.store->StorageId;
            if (have_enabled) {
               mr.Enabled = Enabled;
            }
            if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
               ua->error_msg("%s", db_strerror(ua->db));
            } else {
               ua->info_msg(_(
                 "Catalog record for Volume \"%s\" updated to reference slot %d.\n"),
                 mr.VolumeName, mr.Slot);
            }
         } else {
            ua->info_msg(_("Catalog record for Volume \"%s\" is up to date.\n"),
               mr.VolumeName);
         }
         db_unlock(ua->db);
         continue;
      } else {
         ua->warning_msg(_("Volume \"%s\" not found in catalog. Slot=%d InChanger set to zero.\n"),
             mr.VolumeName, vl->Slot);
      }
      db_unlock(ua->db);
   }
   memset(&mr, 0, sizeof(mr));
   mr.InChanger = 1;
   mr.StorageId = store.store->StorageId;
   db_lock(ua->db);
   for (int i=1; i <= max_slots; i++) {
      if (slot_list[i]) {
         mr.Slot = i;
         /* Set InChanger to zero for this Slot */
         db_make_inchanger_unique(ua->jcr, ua->db, &mr);
      }
   }
   db_unlock(ua->db);

bail_out:

   free_vol_list(vol_list);
   free(slot_list);
   close_sd_bsock(ua);

   return;
}
Esempio n. 5
0
/*
 * Update Slots corresponding to Volumes in autochanger
 */
static void update_slots(UAContext *ua)
{
   USTORERES store;
   vol_list_t *vl;
   dlist *vol_list = NULL;
   MEDIA_DBR mr;
   char *slot_list;
   bool scan;
   int max_slots;
   int drive = -1;
   int Enabled = 1;
   bool have_enabled;
   int i;

   if (!open_client_db(ua)) {
      return;
   }
   store.store = get_storage_resource(ua, true, true);
   if (!store.store) {
      return;
   }
   pm_strcpy(store.store_source, _("command line"));
   set_wstorage(ua->jcr, &store);

   scan = find_arg(ua, NT_("scan")) >= 0;
   if (scan) {
      drive = get_storage_drive(ua, store.store);
   }
   if ((i=find_arg_with_value(ua, NT_("Enabled"))) >= 0) {
      Enabled = get_enabled(ua, ua->argv[i]);
      if (Enabled < 0) {
         return;
      }
      have_enabled = true;
   } else {
      have_enabled = false;
   }

   max_slots = get_num_slots_from_SD(ua);
   Dmsg1(100, "max_slots=%d\n", max_slots);
   if (max_slots <= 0) {
      ua->warning_msg(_("No slots in changer to scan.\n"));
      return;
   }

   slot_list = (char *)malloc(nbytes_for_bits(max_slots));
   clear_all_bits(max_slots, slot_list);
   if (!get_user_slot_list(ua, slot_list, "slots", max_slots)) {
      free(slot_list);
      return;
   }

   vol_list = get_vol_list_from_SD(ua, store.store, false, scan);
   if (!vol_list) {
      ua->warning_msg(_("No Volumes found to update, or no barcodes.\n"));
      goto bail_out;
   }

   /*
    * First zap out any InChanger with StorageId=0
    */
   db_sql_query(ua->db, "UPDATE Media SET InChanger=0 WHERE StorageId=0");

   /*
    * Walk through the list updating the media records
    */
   memset(&mr, 0, sizeof(mr));
   foreach_dlist(vl, vol_list) {
      if (vl->Slot > max_slots) {
         ua->warning_msg(_("Slot %d greater than max %d ignored.\n"), vl->Slot, max_slots);
         continue;
      }
      /*
       * Check if user wants us to look at this slot
       */
      if (!bit_is_set(vl->Slot - 1, slot_list)) {
         Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
         continue;
      }
      /*
       * If scanning, we read the label rather than the barcode
       */
      if (scan) {
         if (vl->VolName) {
            free(vl->VolName);
            vl->VolName = NULL;
         }
         vl->VolName = get_volume_name_from_SD(ua, vl->Slot, drive);
         Dmsg2(100, "Got Vol=%s from SD for Slot=%d\n", vl->VolName, vl->Slot);
      }
      clear_bit(vl->Slot - 1, slot_list); /* clear Slot */
      set_storageid_in_mr(store.store, &mr);
      mr.Slot = vl->Slot;
      mr.InChanger = 1;
      mr.MediaId = 0;                 /* Get by VolumeName */
      if (vl->VolName) {
         bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
      } else {
         mr.VolumeName[0] = 0;
      }
      set_storageid_in_mr(store.store, &mr);

      Dmsg4(100, "Before make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
            mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
      db_lock(ua->db);
      /*
       * Set InChanger to zero for this Slot
       */
      db_make_inchanger_unique(ua->jcr, ua->db, &mr);
      db_unlock(ua->db);
      Dmsg4(100, "After make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
            mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);

      if (!vl->VolName) {
         Dmsg1(100, "No VolName for Slot=%d setting InChanger to zero.\n", vl->Slot);
         ua->info_msg(_("No VolName for Slot=%d InChanger set to zero.\n"), vl->Slot);
         continue;
      }

      db_lock(ua->db);
      Dmsg4(100, "Before get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
            mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
      if (db_get_media_record(ua->jcr, ua->db, &mr)) {
         Dmsg4(100, "After get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
            mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
         /*
          * If Slot, Inchanger, and StorageId have changed, update the Media record
          */
         if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != store.store->StorageId) {
            mr.Slot = vl->Slot;
            mr.InChanger = 1;
            if (have_enabled) {
               mr.Enabled = Enabled;
            }
            set_storageid_in_mr(store.store, &mr);
            if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
               ua->error_msg("%s", db_strerror(ua->db));
            } else {
               ua->info_msg(_("Catalog record for Volume \"%s\" updated to reference slot %d.\n"),
                            mr.VolumeName, mr.Slot);
            }
         } else {
            ua->info_msg(_("Catalog record for Volume \"%s\" is up to date.\n"), mr.VolumeName);
         }
      } else {
         ua->warning_msg(_("Volume \"%s\" not found in catalog. Slot=%d InChanger set to zero.\n"),
                         mr.VolumeName, vl->Slot);
      }
      db_unlock(ua->db);
   }

   memset(&mr, 0, sizeof(mr));
   mr.InChanger = 1;
   set_storageid_in_mr(store.store, &mr);

   /*
    * Any slot not visited gets it Inchanger flag reset.
    */
   db_lock(ua->db);
   for (i = 1; i <= max_slots; i++) {
      if (bit_is_set(i - 1, slot_list)) {
         /*
          * Set InChanger to zero for this Slot
          */
         mr.Slot = i;
         db_make_inchanger_unique(ua->jcr, ua->db, &mr);
      }
   }
   db_unlock(ua->db);

bail_out:
   if (vol_list) {
      free_vol_list(vol_list);
   }
   free(slot_list);
   close_sd_bsock(ua);

   return;
}