示例#1
0
/*
 * Update a media record -- allows you to change the
 *  Volume status. E.g. if you want Bacula to stop
 *  writing on the volume, set it to anything other
 *  than Append.
 */
static int update_volume(UAContext *ua)
{
   MEDIA_DBR mr;
   POOL *pool;
   POOL_DBR pr;
   POOLMEM *query;
   POOL_MEM ret;
   char buf[1000];
   char ed1[130];
   bool done = false;
   int i;
   const char *kw[] = {
      NT_("VolStatus"),                /* 0 */
      NT_("VolRetention"),             /* 1 */
      NT_("VolUse"),                   /* 2 */
      NT_("MaxVolJobs"),               /* 3 */
      NT_("MaxVolFiles"),              /* 4 */
      NT_("MaxVolBytes"),              /* 5 */
      NT_("Recycle"),                  /* 6 */
      NT_("InChanger"),                /* 7 */
      NT_("Slot"),                     /* 8 */
      NT_("Pool"),                     /* 9 */
      NT_("FromPool"),                 /* 10 */
      NT_("AllFromPool"),              /* 11 !!! see below !!! */
      NT_("Enabled"),                  /* 12 */
      NT_("RecyclePool"),              /* 13 */
      NT_("ActionOnPurge"),            /* 14 */
      NULL };

#define AllFromPool 11               /* keep this updated with above */

   for (i=0; kw[i]; i++) {
      int j;
      POOL_DBR pr;

      if ((j=find_arg_with_value(ua, kw[i])) > 0) {
         /* If all from pool don't select a media record */
         if (i != AllFromPool && !select_media_dbr(ua, &mr)) {
            return 0;
         }
         switch (i) {
         case 0:
            update_volstatus(ua, ua->argv[j], &mr);
            break;
         case 1:
            update_volretention(ua, ua->argv[j], &mr);
            break;
         case 2:
            update_voluseduration(ua, ua->argv[j], &mr);
            break;
         case 3:
            update_volmaxjobs(ua, ua->argv[j], &mr);
            break;
         case 4:
            update_volmaxfiles(ua, ua->argv[j], &mr);
            break;
         case 5:
            update_volmaxbytes(ua, ua->argv[j], &mr);
            break;
         case 6:
            update_volrecycle(ua, ua->argv[j], &mr);
            break;
         case 7:
            update_volinchanger(ua, ua->argv[j], &mr);
            break;
         case 8:
            update_volslot(ua, ua->argv[j], &mr);
            break;
         case 9:
            memset(&pr, 0, sizeof(POOL_DBR));
            pr.PoolId = mr.PoolId;
            if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
               ua->error_msg("%s", db_strerror(ua->db));
               break;
            }
            update_vol_pool(ua, ua->argv[j], &mr, &pr);
            break;
         case 10:
            update_vol_from_pool(ua, &mr);
            return 1;
         case 11:
            update_all_vols_from_pool(ua, ua->argv[j]);
            return 1;
         case 12:
            update_volenabled(ua, ua->argv[j], &mr);
            break;
         case 13:
            update_vol_recyclepool(ua, ua->argv[j], &mr);
            break;
         case 14:
            update_vol_actiononpurge(ua, ua->argv[j], &mr);
            break;
         }
         done = true;
      }
   }

   /* Allow user to simply update all volumes */
   if (find_arg(ua, NT_("fromallpools")) > 0) {
      update_all_vols(ua);
      return 1;
   }

   for ( ; !done; ) {
      start_prompt(ua, _("Parameters to modify:\n"));
      add_prompt(ua, _("Volume Status"));              /* 0 */
      add_prompt(ua, _("Volume Retention Period"));    /* 1 */
      add_prompt(ua, _("Volume Use Duration"));        /* 2 */
      add_prompt(ua, _("Maximum Volume Jobs"));        /* 3 */
      add_prompt(ua, _("Maximum Volume Files"));       /* 4 */
      add_prompt(ua, _("Maximum Volume Bytes"));       /* 5 */
      add_prompt(ua, _("Recycle Flag"));               /* 6 */
      add_prompt(ua, _("Slot"));                       /* 7 */
      add_prompt(ua, _("InChanger Flag"));             /* 8 */
      add_prompt(ua, _("Volume Files"));               /* 9 */
      add_prompt(ua, _("Pool"));                       /* 10 */
      add_prompt(ua, _("Volume from Pool"));           /* 11 */
      add_prompt(ua, _("All Volumes from Pool"));      /* 12 */
      add_prompt(ua, _("All Volumes from all Pools")); /* 13 */
      add_prompt(ua, _("Enabled")),                    /* 14 */
      add_prompt(ua, _("RecyclePool")),                /* 15 */
      add_prompt(ua, _("Action On Purge")),            /* 16 */
      add_prompt(ua, _("Done"));                       /* 17 */
      i = do_prompt(ua, "", _("Select parameter to modify"), NULL, 0);  

      /* For All Volumes, All Volumes from Pool, and Done, we don't need
           * a Volume record */
      if ( i != 12 && i != 13 && i != 17) {
         if (!select_media_dbr(ua, &mr)) {  /* Get Volume record */
            return 0;
         }
         ua->info_msg(_("Updating Volume \"%s\"\n"), mr.VolumeName);
      }
      switch (i) {
      case 0:                         /* Volume Status */
         /* Modify Volume Status */
         ua->info_msg(_("Current Volume status is: %s\n"), mr.VolStatus);
         start_prompt(ua, _("Possible Values are:\n"));
         add_prompt(ua, NT_("Append")); 
         add_prompt(ua, NT_("Archive"));
         add_prompt(ua, NT_("Disabled"));
         add_prompt(ua, NT_("Full"));
         add_prompt(ua, NT_("Used"));
         add_prompt(ua, NT_("Cleaning"));
         if (strcmp(mr.VolStatus, NT_("Purged")) == 0) {
            add_prompt(ua, NT_("Recycle"));
         }
         add_prompt(ua, NT_("Read-Only"));
         if (do_prompt(ua, "", _("Choose new Volume Status"), ua->cmd, sizeof(mr.VolStatus)) < 0) {
            return 1;
         }
         update_volstatus(ua, ua->cmd, &mr);
         break;
      case 1:                         /* Retention */
         ua->info_msg(_("Current retention period is: %s\n"),
            edit_utime(mr.VolRetention, ed1, sizeof(ed1)));
         if (!get_cmd(ua, _("Enter Volume Retention period: "))) {
            return 0;
         }
         update_volretention(ua, ua->cmd, &mr);
         break;

      case 2:                         /* Use Duration */
         ua->info_msg(_("Current use duration is: %s\n"),
            edit_utime(mr.VolUseDuration, ed1, sizeof(ed1)));
         if (!get_cmd(ua, _("Enter Volume Use Duration: "))) {
            return 0;
         }
         update_voluseduration(ua, ua->cmd, &mr);
         break;

      case 3:                         /* Max Jobs */
         ua->info_msg(_("Current max jobs is: %u\n"), mr.MaxVolJobs);
         if (!get_pint(ua, _("Enter new Maximum Jobs: "))) {
            return 0;
         }
         update_volmaxjobs(ua, ua->cmd, &mr);
         break;

      case 4:                         /* Max Files */
         ua->info_msg(_("Current max files is: %u\n"), mr.MaxVolFiles);
         if (!get_pint(ua, _("Enter new Maximum Files: "))) {
            return 0;
         }
         update_volmaxfiles(ua, ua->cmd, &mr);
         break;

      case 5:                         /* Max Bytes */
         ua->info_msg(_("Current value is: %s\n"), edit_uint64(mr.MaxVolBytes, ed1));
         if (!get_cmd(ua, _("Enter new Maximum Bytes: "))) {
            return 0;
         }
         update_volmaxbytes(ua, ua->cmd, &mr);
         break;


      case 6:                         /* Recycle */
         ua->info_msg(_("Current recycle flag is: %s\n"),
            mr.Recycle==1?_("yes"):_("no"));
         if (!get_yesno(ua, _("Enter new Recycle status: "))) {
            return 0;
         }
         update_volrecycle(ua, ua->cmd, &mr);
         break;

      case 7:                         /* Slot */
         ua->info_msg(_("Current Slot is: %d\n"), mr.Slot);
         if (!get_pint(ua, _("Enter new Slot: "))) {
            return 0;
         }
         update_volslot(ua, ua->cmd, &mr);
         break;
         
      case 8:                         /* InChanger */
         ua->info_msg(_("Current InChanger flag is: %d\n"), mr.InChanger);
         bsnprintf(buf, sizeof(buf), _("Set InChanger flag for Volume \"%s\": yes/no: "),
            mr.VolumeName);
         if (!get_yesno(ua, buf)) {
            return 0;
         }
         mr.InChanger = ua->pint32_val;
         /*
          * Make sure to use db_update... rather than doing this directly,
          *   so that any Slot is handled correctly.
          */
         if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
            ua->error_msg(_("Error updating media record Slot: ERR=%s"), db_strerror(ua->db));
         } else {
            ua->info_msg(_("New InChanger flag is: %d\n"), mr.InChanger);
         }
         break;


      case 9:                         /* Volume Files */
         int32_t VolFiles;
         ua->warning_msg(_("Warning changing Volume Files can result\n"
                        "in loss of data on your Volume\n\n"));
         ua->info_msg(_("Current Volume Files is: %u\n"), mr.VolFiles);
         if (!get_pint(ua, _("Enter new number of Files for Volume: "))) {
            return 0;
         }
         VolFiles = ua->pint32_val;
         if (VolFiles != (int)(mr.VolFiles + 1)) {
            ua->warning_msg(_("Normally, you should only increase Volume Files by one!\n"));
            if (!get_yesno(ua, _("Increase Volume Files? (yes/no): ")) || ua->pint32_val == 0) {
               break;
            }
         }
         query = get_pool_memory(PM_MESSAGE);
         Mmsg(query, "UPDATE Media SET VolFiles=%u WHERE MediaId=%s",
            VolFiles, edit_int64(mr.MediaId, ed1));
         if (!db_sql_query(ua->db, query, NULL, NULL)) {
            ua->error_msg("%s", db_strerror(ua->db));
         } else {
            ua->info_msg(_("New Volume Files is: %u\n"), VolFiles);
         }
         free_pool_memory(query);
         break;

      case 10:                        /* Volume's Pool */
         memset(&pr, 0, sizeof(POOL_DBR));
         pr.PoolId = mr.PoolId;
         if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
            ua->error_msg("%s", db_strerror(ua->db));
            return 0;
         }
         ua->info_msg(_("Current Pool is: %s\n"), pr.Name);
         if (!get_cmd(ua, _("Enter new Pool name: "))) {
            return 0;
         }
         update_vol_pool(ua, ua->cmd, &mr, &pr);
         return 1;

      case 11:
         update_vol_from_pool(ua, &mr);
         return 1;
      case 12:
         pool = select_pool_resource(ua);
         if (pool) {
            update_all_vols_from_pool(ua, pool->name());
         }
         return 1;

      case 13:
         update_all_vols(ua);
         return 1;

      case 14:
         ua->info_msg(_("Current Enabled is: %d\n"), mr.Enabled);
         if (!get_cmd(ua, _("Enter new Enabled: "))) {
            return 0;
         }
         if (strcasecmp(ua->cmd, "yes") == 0 || strcasecmp(ua->cmd, "true") == 0) {
            mr.Enabled = 1;
         } else if (strcasecmp(ua->cmd, "no") == 0 || strcasecmp(ua->cmd, "false") == 0) {
            mr.Enabled = 0;
         } else if (strcasecmp(ua->cmd, "archived") == 0) { 
            mr.Enabled = 2;
         } else {
            mr.Enabled = atoi(ua->cmd);
         }
         update_volenabled(ua, ua->cmd, &mr);
         break;

      case 15:
         memset(&pr, 0, sizeof(POOL_DBR));
         pr.PoolId = mr.RecyclePoolId;
         if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
            ua->info_msg(_("Current RecyclePool is: %s\n"), pr.Name);
         } else {
            ua->info_msg(_("No current RecyclePool\n"));
         }
         if (!select_pool_dbr(ua, &pr, NT_("recyclepool"))) {
            return 0;
         }
         update_vol_recyclepool(ua, pr.Name, &mr);
         return 1;

      case 16:
         pm_strcpy(ret, "");
         ua->info_msg(_("Current ActionOnPurge is: %s\n"), 
                      action_on_purge_to_string(mr.ActionOnPurge, ret));
         if (!get_cmd(ua, _("Enter new ActionOnPurge (one of: Truncate, None): "))) {
            return 0;
         }

         update_vol_actiononpurge(ua, ua->cmd, &mr);
         break;

      default:                        /* Done or error */
         ua->info_msg(_("Selection terminated.\n"));
         return 1;
      }
   }
   return 1;
}
示例#2
0
/*
 *   Purge records from database
 *
 *     Purge Files (from) [Job|JobId|Client|Volume]
 *     Purge Jobs  (from) [Client|Volume]
 *
 *  N.B. Not all above is implemented yet.
 */
int purgecmd(UAContext *ua, const char *cmd)
{
   int i;
   CLIENTRES *client;
   MEDIA_DBR mr;
   JOB_DBR  jr;
   static const char *keywords[] = {
      NT_("files"),
      NT_("jobs"),
      NT_("volume"),
      NT_("quota"),
      NULL
   };

   static const char *files_keywords[] = {
      NT_("Job"),
      NT_("JobId"),
      NT_("Client"),
      NT_("Volume"),
      NULL
   };

   static const char *quota_keywords[] = {
      NT_("Client"),
      NULL
   };

   static const char *jobs_keywords[] = {
      NT_("Client"),
      NT_("Volume"),
      NULL
   };

   ua->warning_msg(_(
      "\nThis command can be DANGEROUS!!!\n\n"
      "It purges (deletes) all Files from a Job,\n"
      "JobId, Client or Volume; or it purges (deletes)\n"
      "all Jobs from a Client or Volume without regard\n"
      "to retention periods. Normally you should use the\n"
      "PRUNE command, which respects retention periods.\n"));

   if (!open_db(ua)) {
      return 1;
   }
   switch (find_arg_keyword(ua, keywords)) {
   /* Files */
   case 0:
      switch(find_arg_keyword(ua, files_keywords)) {
      case 0:                         /* Job */
      case 1:                         /* JobId */
         if (get_job_dbr(ua, &jr)) {
            char jobid[50];
            edit_int64(jr.JobId, jobid);
            purge_files_from_jobs(ua, jobid);
         }
         return 1;
      case 2:                         /* client */
         client = get_client_resource(ua);
         if (client) {
            purge_files_from_client(ua, client);
         }
         return 1;
      case 3:                         /* Volume */
         if (select_media_dbr(ua, &mr)) {
            purge_files_from_volume(ua, &mr);
         }
         return 1;
      }
   /* Jobs */
   case 1:
      switch(find_arg_keyword(ua, jobs_keywords)) {
      case 0:                         /* client */
         client = get_client_resource(ua);
         if (client) {
            purge_jobs_from_client(ua, client);
         }
         return 1;
      case 1:                         /* Volume */
         if (select_media_dbr(ua, &mr)) {
            purge_jobs_from_volume(ua, &mr, /*force*/true);
         }
         return 1;
      }
   /* Volume */
   case 2:
      /* Perform ActionOnPurge (action=truncate) */
      if (find_arg(ua, "action") >= 0) {
         return action_on_purge_cmd(ua, ua->cmd);
      }

      while ((i=find_arg(ua, NT_("volume"))) >= 0) {
         if (select_media_dbr(ua, &mr)) {
            purge_jobs_from_volume(ua, &mr, /*force*/true);
         }
         *ua->argk[i] = 0;            /* zap keyword already seen */
         ua->send_msg("\n");
      }
      return 1;
   /* Quota */
   case 3:
      switch(find_arg_keyword(ua, quota_keywords)) {
      case 0:                         /* client */
         client = get_client_resource(ua);
         if (client) {
            purge_quota_from_client(ua, client);
         }
         return 1;
      }
   default:
      break;
   }
   switch (do_keyword_prompt(ua, _("Choose item to purge"), keywords)) {
   case 0:                            /* files */
      client = get_client_resource(ua);
      if (client) {
         purge_files_from_client(ua, client);
      }
      break;
   case 1:                            /* jobs */
      client = get_client_resource(ua);
      if (client) {
         purge_jobs_from_client(ua, client);
      }
      break;
   case 2:                            /* Volume */
      if (select_media_dbr(ua, &mr)) {
         purge_jobs_from_volume(ua, &mr, /*force*/true);
      }
      break;
   case 3:
      client = get_client_resource(ua); /* Quota */
      if (client) {
         purge_quota_from_client(ua, client);
      }
   }
   return 1;
}
示例#3
0
文件: ua_label.c 项目: AlD/bareos
/*
 * 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;
}
示例#4
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;
}