]> git.sur5r.net Git - bacula/bacula/commitdiff
- Add Rudolf Cejka's new rc-chio-changer.
authorKern Sibbald <kern@sibbald.com>
Thu, 23 Feb 2006 20:04:22 +0000 (20:04 +0000)
committerKern Sibbald <kern@sibbald.com>
Thu, 23 Feb 2006 20:04:22 +0000 (20:04 +0000)
- Implement pulling Volume from Scratch Pool if the
  Volume is in the autochanger.
- Implement additional command arguments for update Volume.

git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@2811 91ce42f0-d328-0410-95d8-f526ca767f89

bacula/examples/autochangers/rc-chio-changer
bacula/examples/dbdump/sqlite2pgsql [new file with mode: 0755]
bacula/kernstodo
bacula/kes-1.39
bacula/src/dird/next_vol.c
bacula/src/dird/ua_update.c

index 829a5ba6982f739f3a1ed2a755ad287b2ec6b74a..7007842b48646b357b30e366001038733498ae7b 100644 (file)
@@ -1,13 +1,7 @@
 #!/bin/sh
 #
 # Bacula interface to chio autoloader
-#
-# This script was written by Rudolf Cejka
-#   I'm sending rewrite of examples/autochangers/chio-bacula for
-#   FreeBSD under name chio-changer, which tries to save all features
-#   from original code and add the possibility to list real barcodes
-#   from library.  I hope that this version is somewhat nicer.
-#
+# (by Rudolf Cejka <cejkar@fit.vutbr.cz>)
 #
 # $Id$
 #
 # for example (on a FreeBSD system):
 #   chio-changer /dev/ch0 load 1 /dev/nsa0 0
 #
-# If you need an offline, refer to the drive as $4, for example:
-#   mt -f $4 offline
-#
-# Many changers need an offline before the chio unload.
-# Also many changers need to sleep some time after the chio load.
-#
 # If you change the script, take care to return either the chio exit
 # code or a 0. If the script exits with a non-zero exit code, Bacula
 # will assume the request failed.
 #
 
+# Uncomment the following line, if you need to eject a tape before moving
+# it from the drive.
+#OFFLINE=yes
+
+# Uncomment the following line, if you need to wait for some time
+# (in seconds) after (un)loading a tape.
+#SLEEP=10
+
+# Uncomment the following line, if you do not have a changer with volume
+# reader.
+#FAKE_BARCODES=/usr/local/etc/bacula-barcodes
+
 PROGNAME=`basename $0`
 
 usage()
@@ -50,18 +50,13 @@ Example:
 EOF
 }
 
-# This simulates a barcode reader in the changer:
-#FAKE_BARCODES=/usr/local/etc/bacula-barcodes
-
-# Time to wait for (un)loading
-SLEEP=10
-
 # Default settings
 CHANGER=/dev/ch0
 TAPE=/dev/nsa0
 DRIVE=0
 
 CHIO=/bin/chio
+MT=/usr/bin/mt
 
 if [ $# -lt 2 ]; then
   usage
@@ -73,11 +68,6 @@ if [ -n "$1" ]; then
 fi
 COMMAND=$2
 SLOT=$3
-if [ "${SLOT}" = slot ]; then
-  # btape says "... slot 1 drive 0"
-  shift
-  SLOT=$3
-fi
 if [ -n "$4" ]; then
   TAPE=$4
 fi
@@ -87,9 +77,12 @@ fi
 
 case ${COMMAND} in
 unload)
-  # Enable the following line(s) if you need to eject the cartridge.
-  #mt -f ${TAPE} offline
-  #sleep ${SLEEP}
+  if [ "${OFFLINE}" = yes ]; then
+    ${MT} -f ${TAPE} offline
+  fi
+  if [ -n "${SLEEP}" ]; then
+    sleep ${SLEEP}
+  fi
   if [ -z "${SLOT}" ]; then
     ${CHIO} -f ${CHANGER} return drive ${DRIVE}
   else
@@ -108,8 +101,11 @@ unload)
   ;;
 load)
   ${CHIO} -f ${CHANGER} move slot $((${SLOT} - 1)) drive ${DRIVE}
-  # Enable the following line if you need to wait after chio load.
-  #RET=$? ; sleep ${SLEEP} ; exit ${RET}
+  RET=$?
+  if [ -n "${SLEEP}" ]; then
+    sleep ${SLEEP}
+  fi
+  exit ${RET}
   ;;
 list)
   if [ -z "${FAKE_BARCODES}" ]; then
diff --git a/bacula/examples/dbdump/sqlite2pgsql b/bacula/examples/dbdump/sqlite2pgsql
new file mode 100755 (executable)
index 0000000..7423f3d
--- /dev/null
@@ -0,0 +1,146 @@
+#!/bin/bash
+
+# Import an SQLite dump of a Bacula catalog into Postgres
+# Designed for v1.63.3 (as found on Debian sarge)
+#
+# v0.5
+# 
+# Copyright (c) 2006 Russell Howe <russell_howe@wreckage.org>
+
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+# 
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+FILE=bacula.sql
+# Tables, in order of size
+TABLES=(File Filename Path Job Media Pool CDImages Counters Version Client FileSet JobMedia NextId UnsavedFiles BaseFiles)
+# Tables, in insert order
+TABLESINSERT=(Pool CDImages Client Counters FileSet Filename Job Media JobMedia NextId Path File UnsavedFiles Version BaseFiles)
+DBNAME=bacula
+LOGFILE="/var/tmp/sqlite2pgsql.$$.log"
+
+importdata() {
+       if [ "x" == "x$1" ]; then
+               echo "Error: importdata() called without an argument. Aborting."
+               exit 1
+       fi
+       
+       SQLFILE="$1"
+
+       if [ ! -r "$SQLFILE" ]; then
+               echo "Error: Cannot read from $SQLFILE. Aborting."
+               exit 1
+       fi
+
+       echo -n "Loading $SQLFILE into database $DBNAME..."
+       psql -d "$DBNAME" -f "$SQLFILE" || (
+               echo "Failed to load $SQLFILE into database $DBNAME. psql exited with return code $?. Aborting."
+               exit 1
+       )
+}
+
+
+# Go through each of the table names, splitting the INSERT statements off
+# into seperate files
+for table in ${TABLES[@]}; do
+       SRC="$FILE.other"
+       if [ ! -f "$FILE.other" ]; then
+               SRC="$FILE"
+       fi
+       PATTERN="^INSERT INTO $table "
+       if [ ! -f "$FILE.data.$table" ]; then
+               echo -n "Separating $table table from database dump..."
+
+               echo "BEGIN;" > "$FILE.data.$table.tmp"
+               grep "$PATTERN" "$SRC" >> "$FILE.data.$table.tmp"
+               echo "COMMIT;" >> "$FILE.data.$table.tmp"
+               
+               mv "$FILE.data.$table.tmp" "$FILE.data.$table"
+               echo "done. ($FILE.data.$table)"
+               echo -n "Stripping matched lines from the source file to speed up the next round..."
+               grep -v "$PATTERN" "$SRC" > "$FILE.other.tmp"
+               mv "$FILE.other.tmp" "$FILE.other"
+               echo "done."
+       else
+               echo "$FILE.data.$table already exists. Assuming this table has already been split"
+               echo "off from the main dump. Not regenerating."
+       fi
+done
+
+echo "Seperating DDL statements from INSERT statements"
+
+grep -v "^INSERT" "$FILE.other" > "$FILE.ddl"
+echo "DDL statements are now in $FILE.ddl"
+
+grep "^INSERT" "$FILE.other" > "$FILE.data.other"
+echo "Any remaining INSERT statements are now in $FILE.data.other"
+
+echo "Fixing up datatypes used in the DDL..."
+
+sed -e 's/TINYINT/SMALLINT/g' \
+    -e 's/DATETIME/TIMESTAMP/g' \
+    -e 's/INTEGER UNSIGNED/INTEGER/g' \
+    -e 's/BIGINT UNSIGNED/BIGINT/g' \
+    -e 's/INTEGER AUTOINCREMENT/SERIAL/g' \
+    -e s/\ DEFAULT\ \"\"/\ DEFAULT\ \'\'/g \
+    -e s#\ TIMESTAMP\ DEFAULT\ 0#\ TIMESTAMP\ DEFAULT\ \'1/1/1970\'#g "$FILE.ddl" > "$FILE.ddl.postgres"
+
+echo "Fixing Pool table..."
+
+sed -e 's/,0,0);$/,NULL,NULL);/' "$FILE.data.Pool" > "$FILE.data.Pool.fixed"
+
+echo "Fixing removing entries from Job table which no longer have a Pool to link to"
+
+# Remove jobs which refer to nonexistent pools, and fix up invalid start and end times to be 1/1/1970
+grep -vE '([2589]|1[0-5]),[0-9]+,[0-9]+,[0-9]+\);' "$FILE.data.Job" \
+       |sed -e s@^\\\(INSERT\ INTO\ Job\ VALUES\(\\\(\[^,\]\\\+,\\\)\\\{8\\\}\\\)0,@\\1NULL,@ \
+       -e s@^\\\(INSERT\ INTO\ Job\ VALUES\(\\\(\[^,\]\\\+,\\\)\\\{9\\\}\\\)0,@\\1\NULL,@ \
+       -e s@^\\\(INSERT\ INTO\ Job\ VALUES\(\\\(\[^,\]\\\+,\\\)\\\{17\\\}\\\)0,@\\1\NULL,@ \
+       -e s@^\\\(INSERT\ INTO\ Job\ VALUES\(\\\(\[^,\]\\\+,\\\)\\\{18\\\}\\\)0,@\\1\NULL,@ \
+       -e s@^\\\(INSERT\ INTO\ Job\ VALUES\(\\\(\[^,\]\\\+,\\\)\\\{5\\\}\\\)0,@\\1NULL,@ > "$FILE.data.Job.fixed"
+
+# Remove JobMedia entries which refer to nonexistent Jobs
+
+echo "Cleaning up the dump of the JobMedia table..."
+
+grep -vE 'INSERT INTO JobMedia VALUES\([0-9]+,([12589]|1[0-4]),' "$FILE.data.JobMedia" > "$FILE.data.JobMedia.fixed"
+
+# Remove File entries which refer to nonexistent Jobs
+
+echo "Cleaning up the dump of the File table..."
+
+grep -vE 'INSERT INTO File VALUES\([0-9]+,[0-9]+,([12589]|1[0-4]),' "$FILE.data.File" > "$FILE.data.File.fixed"
+
+echo "OK, we should be ready to import data into PostgreSQL now. DDL first..."
+echo "This will probably fail the first time. You will have to edit $FILE.other"
+echo "and rearrange the CREATE TABLE statements so that the tables are created"
+echo "in the correct order."
+echo "After editing $FILE.other, simply rerun this script and it will carry on"
+echo "where it left off."
+
+importdata "$FILE.ddl.postgres"
+
+for table in ${TABLESINSERT[@]} other; do
+       IMPORTFILE="$FILE.data.$table"
+       if [ -f "$FILE.data.$table.fixed" ]; then
+               IMPORTFILE="$FILE.data.$table.fixed"
+       fi
+       importdata "$IMPORTFILE" 2>&1 |tee -a "$LOGFILE"
+done
+
+echo "All done! Check $LOGFILE for errors."
+
index 45a1ce7c33cda99b084cf89f2ed3186068635bfd..99ad0fb52a7dbb3098abcaab8429504b487560b0 100644 (file)
@@ -1,5 +1,5 @@
                     Kern's ToDo List
-                     17 February 2006
+                     22 February 2006
 
 Major development:      
 Project                     Developer
@@ -20,7 +20,9 @@ Priority:
   directive).
 
 For 1.39:
-- Keep same dcr when switching device ...
+- Does Bacula backup Windows shortcuts?
+- Add recycle count to Media record.
+- Add initial write date to Media record.
 - Job retention period in a Pool (and hence Volume).  The job would
   then be migrated.
 - Detect resource deadlock in Migrate when same job wants to read
@@ -1283,3 +1285,4 @@ Block Position: 0
   even defined).
 - Make sure Maximum Volumes is respected in Pools when adding
   Volumes (e.g. when pulling a Scratch volume).
+- Keep same dcr when switching device ...
index 58e8ad1d3ac6cdda867504a4dbf948f1b0f3ea5b..6af59114a5d36d6cd3c70b2026fc25968c987a51 100644 (file)
@@ -3,7 +3,14 @@
 
 General:
 
-Changes to 1.39.5
+Changes to 1.39.6
+23Feb06
+- Add Rudolf Cejka's new rc-chio-changer.
+- Implement pulling Volume from Scratch Pool if the
+  Volume is in the autochanger.
+- Implement additional command arguments for update Volume.
+
+Changes to 1.39.5 
 22Feb06
 - Back port changes to 1.38.5
 - Fix recycle SQL for StorageId.
index e2bcfe616df6b211d530d0d4261388831a3a1504..f27e0cb25fe4f9a4f69df74b1fe638dfee5fa010 100644 (file)
@@ -63,20 +63,27 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, bool create)
        */
       ok = db_find_next_volume(jcr, jcr->db, index, InChanger, mr);
       Dmsg2(100, "catreq after find_next_vol ok=%d FW=%d\n", ok, mr->FirstWritten);
+      /*
+       * 2. Try pulling a Scratch volume if one exists in the autochanger
+       */
+      if (!ok && InChanger) {
+         ok = get_scratch_volume(jcr, mr, InChanger);
+      }
+
       if (!ok) {
          /*
-          * 2. Try finding a recycled volume
+          * 3. Try finding a recycled volume
           */
          ok = find_recycled_volume(jcr, InChanger, mr);
          Dmsg2(100, "find_recycled_volume %d FW=%d\n", ok, mr->FirstWritten);
          if (!ok) {
             /*
-             * 3. Try recycling any purged volume
+             * 4. Try recycling any purged volume
              */
             ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
             if (!ok) {
                /*
-                * 4. Try pruning Volumes
+                * 5. Try pruning Volumes
                 */
                prune_volumes(jcr);
                ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
@@ -91,14 +98,14 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, bool create)
 
          if (!ok) {
             /*
-             * 5. Try pulling a volume from the Scratch pool
+             * 6. Try pulling a volume from the Scratch pool
              */ 
             ok = get_scratch_volume(jcr, mr, InChanger);
          }
 
          if (!ok && create) {
             /*
-             * 6. Try "creating" a new Volume
+             * 7. Try "creating" a new Volume
              */
             ok = newVolume(jcr, mr);
          }
@@ -116,14 +123,14 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, bool create)
                UAContext *ua;
                Dmsg0(400, "Try purge.\n");
                /*
-                * 7.  Try to purging oldest volume only if not UA calling us.
+                * 8.  Try to purging oldest volume only if not UA calling us.
                 */
                ua = new_ua_context(jcr);
                if (jcr->pool->purge_oldest_volume && create) {
                   Jmsg(jcr, M_INFO, 0, _("Purging oldest volume \"%s\"\n"), mr->VolumeName);
                   ok = purge_jobs_from_volume(ua, mr);
                /*
-                * 8. or try recycling the oldest volume
+                * 9. or try recycling the oldest volume
                 */
                } else if (jcr->pool->recycle_oldest_volume) {
                   Jmsg(jcr, M_INFO, 0, _("Pruning oldest volume \"%s\"\n"), mr->VolumeName);
@@ -310,21 +317,6 @@ static bool get_scratch_volume(JCR *jcr, MEDIA_DBR *mr, bool InChanger)
 
    /* Only one thread at a time can pull from the scratch pool */
    P(mutex);
-   /*   
-    * Get pool record where the Scratch Volume will go
-    */
-   memset(&pr, 0, sizeof(pr));
-   bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
-   if (!db_get_pool_record(jcr, jcr->db, &pr)) {
-      Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"), 
-           db_strerror(jcr->db));
-      goto bail_out;
-   }
-   if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
-      Jmsg(jcr, M_WARNING, 0, _("Unable to use Scratch Volume, Pool full MaxVols=%d\n"),
-         pr.MaxVols);
-      goto bail_out;
-   }
    /* 
     * Get Pool record for Scratch Pool
     */
@@ -333,10 +325,32 @@ static bool get_scratch_volume(JCR *jcr, MEDIA_DBR *mr, bool InChanger)
    if (db_get_pool_record(jcr, jcr->db, &spr)) {
       memset(&smr, 0, sizeof(smr));
       smr.PoolId = spr.PoolId;
+      if (InChanger) {       
+         smr.StorageId = mr->StorageId;  /* want only Scratch Volumes in changer */
+      }
       bstrncpy(smr.VolStatus, "Append", sizeof(smr.VolStatus));  /* want only appendable volumes */
       bstrncpy(smr.MediaType, mr->MediaType, sizeof(smr.MediaType));
       if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) {
          POOL_MEM query(PM_MESSAGE);
+
+         /*   
+          * Get pool record where the Scratch Volume will go to ensure
+          * that we can add a Volume.
+          */
+         memset(&pr, 0, sizeof(pr));
+         bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
+         if (!db_get_pool_record(jcr, jcr->db, &pr)) {
+            Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"), 
+                 db_strerror(jcr->db));
+            goto bail_out;
+         }
+         if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
+            Jmsg(jcr, M_WARNING, 0, _("Unable add Scratch Volume, Pool \"%s\" full MaxVols=%d\n"),
+               jcr->pool->hdr.name, pr.MaxVols);
+            goto bail_out;
+         }
+
+         /* OK, now move Scratch Volume */
          db_lock(jcr->db);
          Mmsg(query, "UPDATE Media SET PoolId=%s WHERE MediaId=%s",
               edit_int64(mr->PoolId, ed1),
index c02ab2bca41cbfc8e22e499c3f3104aeea7ed749..8737ecc61ada5ec564a2330c1e4be2c69211d8bc 100644 (file)
@@ -172,76 +172,122 @@ static void update_voluseduration(UAContext *ua, char *val, MEDIA_DBR *mr)
 
 static void update_volmaxjobs(UAContext *ua, char *val, MEDIA_DBR *mr)
 {
-   POOLMEM *query = get_pool_memory(PM_MESSAGE);
+   POOL_MEM query(PM_MESSAGE);
    char ed1[50];
    Mmsg(query, "UPDATE Media SET MaxVolJobs=%s WHERE MediaId=%s",
       val, edit_int64(mr->MediaId,ed1));
-   if (!db_sql_query(ua->db, query, NULL, NULL)) {
+   if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
       bsendmsg(ua, "%s", db_strerror(ua->db));
    } else {
       bsendmsg(ua, _("New max jobs is: %s\n"), val);
    }
-   free_pool_memory(query);
 }
 
 static void update_volmaxfiles(UAContext *ua, char *val, MEDIA_DBR *mr)
 {
-   POOLMEM *query = get_pool_memory(PM_MESSAGE);
+   POOL_MEM query(PM_MESSAGE);
    char ed1[50];
    Mmsg(query, "UPDATE Media SET MaxVolFiles=%s WHERE MediaId=%s",
       val, edit_int64(mr->MediaId, ed1));
-   if (!db_sql_query(ua->db, query, NULL, NULL)) {
+   if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
       bsendmsg(ua, "%s", db_strerror(ua->db));
    } else {
       bsendmsg(ua, _("New max files is: %s\n"), val);
    }
-   free_pool_memory(query);
 }
 
 static void update_volmaxbytes(UAContext *ua, char *val, MEDIA_DBR *mr)
 {
    uint64_t maxbytes;
    char ed1[50], ed2[50];
-   POOLMEM *query;
+   POOL_MEM query(PM_MESSAGE);
 
    if (!size_to_uint64(val, strlen(val), &maxbytes)) {
       bsendmsg(ua, _("Invalid max. bytes specification: %s\n"), val);
       return;
    }
-   query = get_pool_memory(PM_MESSAGE);
    Mmsg(query, "UPDATE Media SET MaxVolBytes=%s WHERE MediaId=%s",
       edit_uint64(maxbytes, ed1), edit_int64(mr->MediaId, ed2));
-   if (!db_sql_query(ua->db, query, NULL, NULL)) {
+   if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
       bsendmsg(ua, "%s", db_strerror(ua->db));
    } else {
       bsendmsg(ua, _("New Max bytes is: %s\n"), edit_uint64(maxbytes, ed1));
    }
-   free_pool_memory(query);
 }
 
 static void update_volrecycle(UAContext *ua, char *val, MEDIA_DBR *mr)
 {
    int recycle;
    char ed1[50];
-   POOLMEM *query;
+   POOL_MEM query(PM_MESSAGE);
    if (strcasecmp(val, _("yes")) == 0) {
       recycle = 1;
    } else if (strcasecmp(val, _("no")) == 0) {
       recycle = 0;
    } else {
-      bsendmsg(ua, _("Invalid value. It must by yes or no.\n"));
+      bsendmsg(ua, _("Invalid value. It must be yes or no.\n"));
       return;
    }
-   query = get_pool_memory(PM_MESSAGE);
    Mmsg(query, "UPDATE Media SET Recycle=%d WHERE MediaId=%s",
       recycle, edit_int64(mr->MediaId, ed1));
-   if (!db_sql_query(ua->db, query, NULL, NULL)) {
+   if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
       bsendmsg(ua, "%s", db_strerror(ua->db));
    } else {
       bsendmsg(ua, _("New Recycle flag is: %s\n"),
          mr->Recycle==1?_("yes"):_("no"));
    }
-   free_pool_memory(query);
+}
+
+static void update_volinchanger(UAContext *ua, char *val, MEDIA_DBR *mr)
+{
+   int InChanger;
+   char ed1[50];
+
+   POOL_MEM query(PM_MESSAGE);
+   if (strcasecmp(val, _("yes")) == 0) {
+      InChanger = 1;
+   } else if (strcasecmp(val, _("no")) == 0) {
+      InChanger = 0;
+   } else {
+      bsendmsg(ua, _("Invalid value. It must be yes or no.\n"));
+      return;
+   }
+   Mmsg(query, "UPDATE Media SET InChanger=%d WHERE MediaId=%s",
+      InChanger, edit_int64(mr->MediaId, ed1));
+   if (!db_sql_query(ua->db, query.c_str(), NULL, NULL)) {
+      bsendmsg(ua, "%s", db_strerror(ua->db));
+   } else {
+      bsendmsg(ua, _("New InChanger flag is: %s\n"),
+         mr->InChanger==1?_("yes"):_("no"));
+   }
+}
+
+
+static void update_volslot(UAContext *ua, char *val, MEDIA_DBR *mr)
+{
+   POOL_DBR pr;
+
+   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));
+      return;
+   }
+   mr->Slot = atoi(val);
+   if (pr.MaxVols > 0 && mr->Slot > (int)pr.MaxVols) {
+      bsendmsg(ua, _("Invalid slot, it must be between 0 and MaxVols=%d\n"),
+         pr.MaxVols);
+      return;
+   }
+   /*
+    * 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)) {
+      bsendmsg(ua, _("Error updating media record Slot: ERR=%s"), db_strerror(ua->db));
+   } else {
+      bsendmsg(ua, _("New Slot is: %d\n"), mr->Slot);
+   }
 }
 
 /* Modify the Pool in which this Volume is located */
@@ -349,16 +395,20 @@ static int update_volume(UAContext *ua)
       _("MaxVolFiles"),              /* 4 */
       _("MaxVolBytes"),              /* 5 */
       _("Recycle"),                  /* 6 */
-      _("Pool"),                     /* 7 */
-      _("FromPool"),                 /* 8 */
-      _("AllFromPool"),              /* 9 */
+      _("InChanger"),                /* 7 */
+      _("Slot"),                     /* 8 */
+      _("Pool"),                     /* 9 */
+      _("FromPool"),                 /* 10 */
+      _("AllFromPool"),              /* 11 !!! see below !!! */
       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 (i != 9 && !select_media_dbr(ua, &mr)) {
+         if (i != AllFromPool && !select_media_dbr(ua, &mr)) {
             return 0;
          }
          switch (i) {
@@ -384,6 +434,12 @@ static int update_volume(UAContext *ua)
             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)) {
@@ -392,10 +448,10 @@ static int update_volume(UAContext *ua)
             }
             update_vol_pool(ua, ua->argv[j], &mr, &pr);
             break;
-         case 8:
+         case 10:
             update_vol_from_pool(ua, &mr);
             return 1;
-         case 9:
+         case 11:
             update_all_vols_from_pool(ua);
             return 1;
          }
@@ -500,36 +556,13 @@ static int update_volume(UAContext *ua)
          break;
 
       case 7:                         /* Slot */
-         int Slot;
-
-         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));
-            return 0;
-         }
          bsendmsg(ua, _("Current Slot is: %d\n"), mr.Slot);
          if (!get_pint(ua, _("Enter new Slot: "))) {
             return 0;
          }
-         Slot = ua->pint32_val;
-         if (pr.MaxVols > 0 && Slot > (int)pr.MaxVols) {
-            bsendmsg(ua, _("Invalid slot, it must be between 0 and MaxVols=%d\n"),
-               pr.MaxVols);
-            break;
-         }
-         mr.Slot = Slot;
-         /*
-          * 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)) {
-            bsendmsg(ua, _("Error updating media record Slot: ERR=%s"), db_strerror(ua->db));
-         } else {
-            bsendmsg(ua, _("New Slot is: %d\n"), mr.Slot);
-         }
+         update_volslot(ua, ua->cmd, &mr);
          break;
-
+         
       case 8:                         /* InChanger */
          bsendmsg(ua, _("Current InChanger flag is: %d\n"), mr.InChanger);
          if (!get_yesno(ua, _("Set InChanger flag? yes/no: "))) {