]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/dird/ua_cmds.c
Fix bconsole bug with new BNET signals
[bacula/bacula] / bacula / src / dird / ua_cmds.c
index 40d43412ce86f661c9970a3cd0fb61961ee83b53..8f99ee8b934b92342e3ef93fb087387f16e0aa6b 100644 (file)
@@ -1,12 +1,12 @@
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
+   Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
 
    The main author of Bacula is Kern Sibbald, with contributions from
    many others, a complete list can be found in the file AUTHORS.
    This program is Free Software; you can redistribute it and/or
-   modify it under the terms of version two of the GNU General Public
+   modify it under the terms of version three of the GNU Affero General Public
    License as published by the Free Software Foundation and included
    in the file LICENSE.
 
@@ -15,7 +15,7 @@
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    General Public License for more details.
 
-   You should have received a copy of the GNU General Public License
+   You should have received a copy of the GNU Affero General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
 #include "bacula.h"
 #include "dird.h"
 
+#ifdef HAVE_PYTHON
+
+#undef _POSIX_C_SOURCE
+#include <Python.h>
+
+#include "lib/pythonlib.h"
+
+/* Imported Functions */
+extern PyObject *job_getattr(PyObject *self, char *attrname);
+extern int job_setattr(PyObject *self, char *attrname, PyObject *value);
+
+#endif /* HAVE_PYTHON */
+
 /* Imported subroutines */
 
 /* Imported variables */
@@ -87,7 +100,7 @@ static int version_cmd(UAContext *ua, const char *cmd);
 static int wait_cmd(UAContext *ua, const char *cmd);
 
 static void do_job_delete(UAContext *ua, JobId_t JobId);
-static void delete_job_id_range(UAContext *ua, char *tok);
+static bool delete_job_id_range(UAContext *ua, char *tok);
 static int delete_volume(UAContext *ua);
 static int delete_pool(UAContext *ua);
 static void delete_job(UAContext *ua);
@@ -95,60 +108,106 @@ static void delete_job(UAContext *ua);
 int qhelp_cmd(UAContext *ua, const char *cmd);
 int quit_cmd(UAContext *ua, const char *cmd);
 
-
-struct cmdstruct { const char *key; int (*func)(UAContext *ua, const char *cmd); const char *help; };
-static struct cmdstruct commands[] = {
- { NT_("add"),        add_cmd,         _("add media to a pool")},
- { NT_("autodisplay"), autodisplay_cmd, _("autodisplay [on|off] -- console messages")},
- { NT_("automount"),   automount_cmd,  _("automount [on|off] -- after label")},
- { NT_("cancel"),     cancel_cmd,    _("cancel [<jobid=nnn> | <job=name>] -- cancel a job")},
- { NT_("create"),     create_cmd,    _("create DB Pool from resource")},
- { NT_("delete"),     delete_cmd,    _("delete [pool=<pool-name> | media volume=<volume-name>]")},
- { NT_("disable"),    disable_cmd,   _("disable <job=name> -- disable a job")},
- { NT_("enable"),     enable_cmd,    _("enable <job=name> -- enable a job")},
- { NT_("estimate"),   estimate_cmd,  _("performs FileSet estimate, listing gives full listing")},
- { NT_("exit"),       quit_cmd,      _("exit = quit")},
- { NT_("gui"),        gui_cmd,       _("gui [on|off] -- non-interactive gui mode")},
- { NT_("help"),       help_cmd,      _("print this command")},
- { NT_("list"),       list_cmd,      _("list [pools | jobs | jobtotals | media <pool=pool-name> | files <jobid=nn>]; from catalog")},
- { NT_("label"),      label_cmd,     _("label a tape")},
- { NT_("llist"),      llist_cmd,     _("full or long list like list command")},
- { NT_("messages"),   messagescmd,   _("messages")},
- { NT_("memory"),     memory_cmd,    _("print current memory usage")},
- { NT_("mount"),      mount_cmd,     _("mount <storage-name>")},
- { NT_("prune"),      prunecmd,      _("prune expired records from catalog")},
- { NT_("purge"),      purgecmd,      _("purge records from catalog")},
- { NT_("python"),     python_cmd,    _("python control commands")},
- { NT_("quit"),       quit_cmd,      _("quit")},
- { NT_("query"),      querycmd,      _("query catalog")},
- { NT_("restore"),    restore_cmd,   _("restore files")},
- { NT_("relabel"),    relabel_cmd,   _("relabel a tape")},
- { NT_("release"),    release_cmd,   _("release <storage-name>")},
- { NT_("reload"),     reload_cmd,    _("reload conf file")},
- { NT_("run"),        run_cmd,       _("run <job-name>")},
- { NT_("status"),     status_cmd,    _("status [storage | client]=<name>")},
- { NT_("setdebug"),   setdebug_cmd,  _("sets debug level")},
- { NT_("setip"),      setip_cmd,     _("sets new client address -- if authorized")},
- { NT_("show"),       show_cmd,      _("show (resource records) [jobs | pools | ... | all]")},
- { NT_("sqlquery"),   sqlquerycmd,   _("use SQL to query catalog")},
- { NT_("time"),       time_cmd,      _("print current time")},
- { NT_("trace"),      trace_cmd,     _("turn on/off trace to file")},
- { NT_("unmount"),    unmount_cmd,   _("unmount <storage-name>")},
- { NT_("umount"),     unmount_cmd,   _("umount <storage-name> for old-time Unix guys")},
- { NT_("update"),     update_cmd,    _("update Volume, Pool or slots")},
- { NT_("use"),        use_cmd,       _("use catalog xxx")},
- { NT_("var"),        var_cmd,       _("does variable expansion")},
- { NT_("version"),    version_cmd,   _("print Director version")},
- { NT_("wait"),       wait_cmd,      _("wait until no jobs are running [<jobname=name> | <jobid=nnn> | <ujobid=complete_name>]")},
-             };
-#define comsize (sizeof(commands)/sizeof(struct cmdstruct))
+/* not all in alphabetical order.  New commands are added after existing commands with similar letters
+   to prevent breakage of existing user scripts.  */
+struct cmdstruct { 
+   const char *key;                             /* command */
+   int (*func)(UAContext *ua, const char *cmd); /* handler */
+   const char *help;            /* main purpose */
+   const char *usage;           /* all arguments to build usage */
+   const bool use_in_rs;        /* Can use it in Console RunScript */
+};
+static struct cmdstruct commands[] = {                                      /* Can use it in Console RunScript*/
+ { NT_("add"),        add_cmd,     _("Add media to a pool"),   NT_("pool=<pool-name> storage=<storage> jobid=<JobId>"),  false},
+ { NT_("autodisplay"), autodisplay_cmd,_("Autodisplay console messages"), NT_("on | off"),    false},
+ { NT_("automount"),   automount_cmd,  _("Automount after label"),        NT_("on | off"),    false},
+ { NT_("cancel"),     cancel_cmd,    _("Cancel a job"), NT_("jobid=<number> job=<job-name> ujobid=<unique-jobid>"), false},
+ { NT_("create"),     create_cmd,    _("Create DB Pool from resource"), NT_("pool=<pool-name>"),                    false},
+ { NT_("delete"),     delete_cmd,    _("Delete volume, pool or job"), NT_("volume=<vol-name> pool=<pool-name> jobid=<id>"), true},
+ { NT_("disable"),    disable_cmd,   _("Disable a job"), NT_("job=<name>"),        true},
+ { NT_("enable"),     enable_cmd,    _("Enable a job"), NT_("job=<name>"),          true},
+ { NT_("estimate"),   estimate_cmd,  _("Performs FileSet estimate, listing gives full listing"), 
+   NT_("fileset=<fs> client=<cli> level=<level> accurate=<yes/no> job=<job> listing"), true},
+
+ { NT_("exit"),       quit_cmd,      _("Terminate Bconsole session"), NT_(""),         false},
+ { NT_("gui"),        gui_cmd,       _("Non-interactive gui mode"),   NT_("on | off"), false},
+ { NT_("help"),       help_cmd,      _("Print help on specific command"),  
+   NT_("add autodisplay automount cancel create delete disable\n\tenable estimate exit gui label list llist"
+       "\n\tmessages memory mount prune purge python quit query\n\trestore relabel release reload run status"
+       "\n\tsetdebug setip show sqlquery time trace unmount umount\n\tupdate use var version wait"),         false},
+
+ { NT_("label"),      label_cmd,     _("Label a tape"), NT_("storage=<storage> volume=<vol> pool=<pool>"), false},
+ { NT_("list"),       list_cmd,      _("List objects from catalog"), 
+   NT_("pools | jobs | jobtotals | volume | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn>"), true},
+
+ { NT_("llist"),      llist_cmd,     _("Full or long list like list command"),
+   NT_("pools | jobs | jobtotals | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn>"), true},
+
+ { NT_("messages"),   messagescmd,   _("Display pending messages"),   NT_(""),    false},
+ { NT_("memory"),     memory_cmd,    _("Print current memory usage"), NT_(""),    true},
+ { NT_("mount"),      mount_cmd,     _("Mount storage"), 
+   NT_("storage=<storage-name> slot=<num> drive=<num> [ jobid=<id> | job=<job-name> ]"), false},
+
+ { NT_("prune"),      prunecmd,      _("Prune expired records from catalog"), 
+   NT_("files | jobs | pool=<pool> | client=<client-name> | volume=<volume-name> "), true},
+
+ { NT_("purge"),      purgecmd,      _("Purge records from catalog"), NT_("files jobs volume=<vol> [action=<action> devicetype=<type> pool=<pool> allpools storage=<st> drive=<num>]"),  true},
+ { NT_("python"),     python_cmd,    _("Python control commands"),    NT_(""),              false},
+ { NT_("quit"),       quit_cmd,      _("Terminate Bconsole session"), NT_(""),              false},
+ { NT_("query"),      querycmd,      _("Query catalog"),              NT_(""),              false},
+ { NT_("restore"),    restore_cmd,   _("Restore files"), 
+   NT_("where=</path> client=<client> storage=<storage> bootstrap=<file>"
+       "\n\tcomment=<text> jobid=<jobid> done select all"), false},
+
+ { NT_("relabel"),    relabel_cmd,   _("Relabel a tape"), 
+   NT_("storage=<storage-name> oldvolume=<old-volume-name>\n\tvolume=<newvolume-name> pool=<pool>"), false},
+
+ { NT_("release"),    release_cmd,   _("Release storage"),  NT_("storage-name"),      false},
+ { NT_("reload"),     reload_cmd,    _("Reload conf file"), NT_(""),                  true},
+ { NT_("run"),        run_cmd,       _("Run a job"), 
+   NT_("job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
+       "where=<directory-prefix>\n\twhen=<universal-time-specification>\n\tcomment=<text> yes"), false}, 
+
+ { NT_("status"),     status_cmd,    _("Report status"), 
+   NT_("all | dir=<dir-name> | director | client=<client-name> | storage=<storage-name> slots | days=nnn"), true},
+
+ { NT_("setdebug"),   setdebug_cmd,  _("Sets debug level"), 
+   NT_("level=<nn> trace=0/1 client=<client-name> | dir | storage=<storage-name> | all"), true},
+
+ { NT_("setip"),      setip_cmd,     _("Sets new client address -- if authorized"), NT_(""),   false},
+ { NT_("show"),       show_cmd,      _("Show resource records"), 
+   NT_("job=<xxx> |  pool=<yyy> | fileset=<aaa> schedule=<sss> | client=<zzz> | disabled | all"), true},
+
+ { NT_("sqlquery"),   sqlquerycmd,   _("Use SQL to query catalog"), NT_(""),          false},
+ { NT_("time"),       time_cmd,      _("Print current time"),       NT_(""),          true},
+ { NT_("trace"),      trace_cmd,     _("Turn on/off trace to file"), NT_("on | off"), true},
+ { NT_("unmount"),    unmount_cmd,   _("Unmount storage"), 
+   NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
+
+ { NT_("umount"),     unmount_cmd,   _("Umount - for old-time Unix guys, see unmount"), 
+   NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
+
+ { NT_("update"),     update_cmd,    _("Update volume, pool or stats"), 
+   NT_("stats\n\tpool=<poolname>\n\tslots storage=<storage> scan"
+       "\n\tvolume=<volname> volstatus=<status> volretention=<time-def>"
+       "\n\t pool=<pool> recycle=<yes/no> slot=<number>\n\t inchanger=<yes/no>"
+       "\n\t maxvolbytes=<size> maxvolfiles=<nb> maxvoljobs=<nb>"
+       "\n\t enable=<yes/no> recyclepool=<pool> actiononpurge=<action>"),true},
+ { NT_("use"),        use_cmd,       _("Use catalog xxx"), NT_(""),     false},
+ { NT_("var"),        var_cmd,       _("Does variable expansion"), NT_(""),  false},
+ { NT_("version"),    version_cmd,   _("Print Director version"),  NT_(""),  true},
+ { NT_("wait"),       wait_cmd,      _("Wait until no jobs are running"), 
+   NT_("jobname=<name> | jobid=<nnn> | ujobid=<complete_name>"), false}
+};
+
+#define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
 
 /*
  * Execute a command from the UA
  */
 bool do_a_command(UAContext *ua)
 {
-   unsigned int i;
+   int i;
    int len;
    bool ok = false;
    bool found = false;
@@ -160,11 +219,9 @@ bool do_a_command(UAContext *ua)
       return false;
    }
 
-#ifdef xxxx
    while (ua->jcr->wstorage->size()) {
       ua->jcr->wstorage->remove(0);
    }
-#endif
 
    len = strlen(ua->argk[0]);
    for (i=0; i<comsize; i++) {     /* search for command */
@@ -174,8 +231,14 @@ bool do_a_command(UAContext *ua)
              !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
             break;
          }
+         /* Check if this command is authorized in RunScript */
+         if (ua->runscript && !commands[i].use_in_rs) {
+            ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
+            break;
+         }
          if (ua->api) user->signal(BNET_CMD_BEGIN);
          ok = (*commands[i].func)(ua, ua->cmd);   /* go execute command */
+         if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
          found = true;
          break;
       }
@@ -184,7 +247,6 @@ bool do_a_command(UAContext *ua)
       ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
       ok = false;
    }
-   if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
    return ok;
 }
 
@@ -200,6 +262,7 @@ void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
    mr->Recycle = pr->Recycle;
    mr->VolRetention = pr->VolRetention;
    mr->VolUseDuration = pr->VolUseDuration;
+   mr->ActionOnPurge = pr->ActionOnPurge;
    mr->RecyclePoolId = pr->RecyclePoolId;
    mr->MaxVolJobs = pr->MaxVolJobs;
    mr->MaxVolFiles = pr->MaxVolFiles;
@@ -395,10 +458,10 @@ static int cancel_cmd(UAContext *ua, const char *cmd)
    for (i=1; i<ua->argc; i++) {
       if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
          uint32_t JobId;
-         if (!ua->argv[i]) {
+         JobId = str_to_int64(ua->argv[i]);
+         if (!JobId) {
             break;
          }
-         JobId = str_to_int64(ua->argv[i]);
          if (!(jcr=get_jcr_by_id(JobId))) {
             ua->error_msg(_("JobId %s is not running. Use Job name to cancel inactive jobs.\n"),  ua->argv[i]);
             return 1;
@@ -540,6 +603,7 @@ void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
    pr->MaxVolFiles = pool->MaxVolFiles;
    pr->MaxVolBytes = pool->MaxVolBytes;
    pr->AutoPrune = pool->AutoPrune;
+   pr->ActionOnPurge = pool->action_on_purge;
    pr->Recycle = pool->Recycle;
    if (pool->label_format) {
       bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
@@ -548,12 +612,12 @@ void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
    }
 }
 
-/* set/update Pool.RecyclePoolId in Catalog */
-int update_pool_recyclepool(JCR *jcr, B_DB *db, POOL *pool)
+/* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
+int update_pool_references(JCR *jcr, B_DB *db, POOL *pool)
 {
    POOL_DBR  pr;
 
-   if (!pool->RecyclePool) {
+   if (!pool->RecyclePool && !pool->ScratchPool) {
       return 1;
    }
 
@@ -566,7 +630,7 @@ int update_pool_recyclepool(JCR *jcr, B_DB *db, POOL *pool)
 
    set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
 
-   if (!set_pooldbr_recyclepoolid(jcr, db, &pr, pool)) {
+   if (!set_pooldbr_references(jcr, db, &pr, pool)) {
       return -1;                      /* error */
    }
 
@@ -576,10 +640,10 @@ int update_pool_recyclepool(JCR *jcr, B_DB *db, POOL *pool)
    return 1;
 }
 
-/* set POOL_DBR.RecyclePoolId from Pool resource 
+/* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource 
  * works with set_pooldbr_from_poolres
  */
-bool set_pooldbr_recyclepoolid(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
+bool set_pooldbr_references(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
 {
    POOL_DBR rpool;
    bool ret = true;
@@ -601,6 +665,24 @@ bool set_pooldbr_recyclepoolid(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
    } else {                    /* no RecyclePool used, set it to 0 */
       pr->RecyclePoolId = 0;
    }
+
+   if (pool->ScratchPool) {
+      memset(&rpool, 0, sizeof(POOL_DBR));
+
+      bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
+      if (db_get_pool_record(jcr, db, &rpool)) {
+        pr->ScratchPoolId = rpool.PoolId;
+      } else {
+        Jmsg(jcr, M_WARNING, 0,
+        _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
+          "Try to update it with 'update pool=%s'\n"),
+        pool->name(), rpool.Name, rpool.Name,pool->name());
+        ret = false;
+      }
+   } else {                    /* no ScratchPool used, set it to 0 */
+      pr->ScratchPoolId = 0;
+   }
    return ret;
 }
 
@@ -625,12 +707,14 @@ int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
       /* Pool Exists */
       if (op == POOL_OP_UPDATE) {  /* update request */
          set_pooldbr_from_poolres(&pr, pool, op);
+         set_pooldbr_references(jcr, db, &pr, pool);
          db_update_pool_record(jcr, db, &pr);
       }
       return 0;                       /* exists */
    }
 
    set_pooldbr_from_poolres(&pr, pool, op);
+   set_pooldbr_references(jcr, db, &pr, pool);
 
    if (!db_create_pool_record(jcr, db, &pr)) {
       return -1;                      /* error */
@@ -676,6 +760,7 @@ static int create_cmd(UAContext *ua, const char *cmd)
 
 
 extern DIRRES *director;
+extern char *configfile;
 
 /*
  * Python control command
@@ -683,14 +768,29 @@ extern DIRRES *director;
  */
 static int python_cmd(UAContext *ua, const char *cmd)
 {
+#ifdef HAVE_PYTHON
+   init_python_interpreter_args python_args;
+
    if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
       term_python_interpreter();
-      init_python_interpreter(director->name(), 
-         director->scripts_directory, "DirStartUp");
+
+      python_args.progname = director->name();
+      python_args.scriptdir = director->scripts_directory;
+      python_args.modulename = "DirStartUp";
+      python_args.configfile = configfile;
+      python_args.workingdir = director->working_directory;
+      python_args.job_getattr = job_getattr;
+      python_args.job_setattr = job_setattr;
+
+      init_python_interpreter(&python_args);
+
       ua->send_msg(_("Python interpreter restarted.\n"));
    } else {
+#endif /* HAVE_PYTHON */
       ua->warning_msg(_("Nothing done.\n"));
+#ifdef HAVE_PYTHON
    }
+#endif /* HAVE_PYTHON */
    return 1;
 }
 
@@ -736,7 +836,7 @@ static void do_en_disable_cmd(UAContext *ua, bool setting)
 
    i = find_arg_with_value(ua, NT_("job")); 
    if (i < 0) { 
-      job = select_job_resource(ua);
+      job = select_enable_disable_job_resource(ua, setting);
       if (!job) {
          return;
       }
@@ -790,12 +890,12 @@ static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trac
    }
    Dmsg0(120, _("Connected to storage daemon\n"));
    sd = jcr->store_bsock;
-   bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
-   if (bnet_recv(sd) >= 0) {
+   sd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
+   if (sd->recv() >= 0) {
       ua->send_msg("%s", sd->msg);
    }
-   bnet_sig(sd, BNET_TERMINATE);
-   bnet_close(sd);
+   sd->signal(BNET_TERMINATE);
+   sd->close();
    jcr->store_bsock = NULL;
    return;
 }
@@ -816,12 +916,12 @@ static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int tra
    }
    Dmsg0(120, "Connected to file daemon\n");
    fd = ua->jcr->file_bsock;
-   bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
-   if (bnet_recv(fd) >= 0) {
+   fd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
+   if (fd->recv() >= 0) {
       ua->send_msg("%s", fd->msg);
    }
-   bnet_sig(fd, BNET_TERMINATE);
-   bnet_close(fd);
+   fd->signal(BNET_TERMINATE);
+   fd->close();
    ua->jcr->file_bsock = NULL;
    return;
 }
@@ -916,9 +1016,6 @@ static int setdebug_cmd(UAContext *ua, const char *cmd)
    int trace_flag = -1;
    int i;
 
-   if (!open_client_db(ua)) {
-      return 1;
-   }
    Dmsg1(120, "setdebug:%s:\n", cmd);
 
    level = -1;
@@ -1074,6 +1171,7 @@ static int estimate_cmd(UAContext *ua, const char *cmd)
    int listing = 0;
    char since[MAXSTRING];
    JCR *jcr = ua->jcr;
+   int accurate=-1;
 
    jcr->set_JobLevel(L_FULL);
    for (int i=1; i<ua->argc; i++) {
@@ -1085,6 +1183,10 @@ static int estimate_cmd(UAContext *ua, const char *cmd)
                ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
                return 1;
             }
+            if (!acl_access_ok(ua, Client_ACL, client->name())) {
+               ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
+               return 1;
+            }
             continue;
          } else {
             ua->error_msg(_("Client name missing.\n"));
@@ -1141,6 +1243,12 @@ static int estimate_cmd(UAContext *ua, const char *cmd)
            return 1;
          }
       }
+      if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
+         if (!is_yesno(ua->argv[i], &accurate)) {
+            ua->error_msg(_("Invalid value for accurate. " 
+                            "It must be yes or no.\n"));
+         }
+      }
    }
    if (!job && !(client && fileset)) {
       if (!(job = select_job_resource(ua))) {
@@ -1207,10 +1315,26 @@ static int estimate_cmd(UAContext *ua, const char *cmd)
       goto bail_out;
    }
 
+   /* The level string change if accurate mode is enabled */
+   if (accurate >= 0) {
+      jcr->accurate = accurate;
+   } else {
+      jcr->accurate = job->accurate;
+   }
+
    if (!send_level_command(jcr)) {
       goto bail_out;
    }
 
+   /*
+    * If the job is in accurate mode, we send the list of
+    * all files to FD.
+    */
+   Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
+   if (!send_accurate_current_files(jcr)) {
+      goto bail_out;
+   }
+
    bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
    while (bnet_recv(jcr->file_bsock) >= 0) {
       ua->send_msg("%s", jcr->file_bsock->msg);
@@ -1218,8 +1342,8 @@ static int estimate_cmd(UAContext *ua, const char *cmd)
 
 bail_out:
    if (jcr->file_bsock) {
-      bnet_sig(jcr->file_bsock, BNET_TERMINATE);
-      bnet_close(jcr->file_bsock);
+      jcr->file_bsock->signal(BNET_TERMINATE);
+      jcr->file_bsock->close();
       jcr->file_bsock = NULL;
    }
    return 1;
@@ -1338,9 +1462,7 @@ static void delete_job(UAContext *ua)
         sep = strchr(tok, ',');
         while (sep != NULL) {
            *sep = '\0';
-           if (strchr(tok, '-')) {
-               delete_job_id_range(ua, tok);
-           } else {
+           if (!delete_job_id_range(ua, tok)) {
               JobId = str_to_int64(tok);
               do_job_delete(ua, JobId);
            }
@@ -1348,9 +1470,7 @@ static void delete_job(UAContext *ua)
            sep = strchr(tok, ',');
         }
         /* pick up the last token */
-        if (strchr(tok, '-')) {
-            delete_job_id_range(ua, tok);
-        } else {
+        if (!delete_job_id_range(ua, tok)) {
             JobId = str_to_int64(tok);
             do_job_delete(ua, JobId);
         }
@@ -1371,12 +1491,15 @@ static void delete_job(UAContext *ua)
 /*
  * we call delete_job_id_range to parse range tokens and iterate over ranges
  */
-static void delete_job_id_range(UAContext *ua, char *tok)
+static bool delete_job_id_range(UAContext *ua, char *tok)
 {
    char *tok2;
    JobId_t j,j1,j2;
 
    tok2 = strchr(tok, '-');
+   if (!tok2) {
+      return false;
+   }
    *tok2 = '\0';
    tok2++;
    j1 = str_to_int64(tok);
@@ -1384,6 +1507,7 @@ static void delete_job_id_range(UAContext *ua, char *tok)
    for (j=j1; j<=j2; j++) {
       do_job_delete(ua, j);
    }
+   return true;
 }
 
 /*
@@ -1730,6 +1854,7 @@ int wait_cmd(UAContext *ua, const char *cmd)
       status = 2 ;         /* Critical */
       break;
 
+   case JS_Warnings:
    case JS_Terminated:
       status = 0 ;         /* Ok */
       break;
@@ -1754,11 +1879,21 @@ int wait_cmd(UAContext *ua, const char *cmd)
 
 static int help_cmd(UAContext *ua, const char *cmd)
 {
-   unsigned int i;
-
-   ua->send_msg(_("  Command    Description\n  =======    ===========\n"));
+   int i;
+   ua->send_msg(_("  Command       Description\n  =======       ===========\n"));
    for (i=0; i<comsize; i++) {
-      ua->send_msg(_("  %-10s %s\n"), _(commands[i].key), _(commands[i].help));
+      if (ua->argc == 2) {
+         if (!strcasecmp(ua->argk[1], commands[i].key)) {
+            ua->send_msg(_("  %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key, 
+                         commands[i].help, commands[i].usage);
+            break;
+         }
+      } else {
+         ua->send_msg(_("  %-13s %s\n"), commands[i].key, commands[i].help);
+      }
+   }
+   if (i == comsize && ua->argc == 2) {
+      ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
    }
    ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
    return 1;
@@ -1766,10 +1901,29 @@ static int help_cmd(UAContext *ua, const char *cmd)
 
 int qhelp_cmd(UAContext *ua, const char *cmd)
 {
-   unsigned int i;
-
+   int i,j;
+   /* Want to display only commands */
+   j = find_arg(ua, NT_("all"));
+   if (j >= 0) {
+      for (i=0; i<comsize; i++) {
+         ua->send_msg("%s\n", commands[i].key);
+      }
+      return 1;
+   }
+   /* Want to display a specific help section */
+   j = find_arg_with_value(ua, NT_("item"));
+   if (j >= 0 && ua->argk[j]) {
+      for (i=0; i<comsize; i++) {
+         if (bstrcmp(commands[i].key, ua->argv[j])) {
+            ua->send_msg("%s\n", commands[i].usage);
+            break;
+         }
+      }
+      return 1;
+   }
+   /* Want to display everything */
    for (i=0; i<comsize; i++) {
-      ua->send_msg("%s %s\n", commands[i].key, _(commands[i].help));
+      ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
    }
    return 1;
 }
@@ -1777,8 +1931,8 @@ int qhelp_cmd(UAContext *ua, const char *cmd)
 #if 1 
 static int version_cmd(UAContext *ua, const char *cmd)
 {
-   ua->send_msg(_("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
-            HOST_OS, DISTNAME, DISTVER);
+   ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
+                HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
    return 1;
 }
 #else
@@ -1801,6 +1955,22 @@ static int version_cmd(UAContext *ua, const char *cmd)
 }
 #endif
 
+/* 
+ * This call uses open_client_db() and force a
+ * new dedicated connection to the catalog
+ */
+bool open_new_client_db(UAContext *ua)
+{   
+   bool ret;
+
+   /* Force a new dedicated connection */
+   close_db(ua);
+   ua->force_mult_db_connections = true;
+   ret = open_client_db(ua);
+   ua->force_mult_db_connections = false;
+   return ret;
+}
+
 /* 
  * This call explicitly checks for a catalog=xxx and
  *  if given, opens that catalog.  It also checks for
@@ -1886,6 +2056,8 @@ bool open_client_db(UAContext *ua)
  */
 bool open_db(UAContext *ua)
 {
+   bool mult_db_conn;
+
    if (ua->db) {
       return true;
    }
@@ -1897,6 +2069,12 @@ bool open_db(UAContext *ua)
       }
    }
 
+   /* Some modules like bvfs need their own catalog connection */
+   mult_db_conn = ua->catalog->mult_db_connections;
+   if (ua->force_mult_db_connections) {
+      mult_db_conn = true;
+   }
+
    ua->jcr->catalog = ua->catalog;
 
    Dmsg0(100, "UA Open database\n");
@@ -1904,7 +2082,7 @@ bool open_db(UAContext *ua)
                              ua->catalog->db_user,
                              ua->catalog->db_password, ua->catalog->db_address,
                              ua->catalog->db_port, ua->catalog->db_socket,
-                             ua->catalog->mult_db_connections);
+                             mult_db_conn);
    if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
       ua->error_msg(_("Could not open catalog database \"%s\".\n"),
                  ua->catalog->db_name);