]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/dird/ua_cmds.c
Adjust cmdstruct:
[bacula/bacula] / bacula / src / dird / ua_cmds.c
index 91a96a72e7b0304e9237cf0971ff4f05b5b23c96..c8db5126ad6c5382e79b254cc5e5840acd054b5e 100644 (file)
@@ -1,14 +1,14 @@
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
+   Copyright (C) 2000-2008 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
-   License as published by the Free Software Foundation plus additions
-   that are listed in the file LICENSE.
+   License as published by the Free Software Foundation and included
+   in the file LICENSE.
 
    This program is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -20,7 +20,7 @@
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
 
-   Bacula® is a registered trademark of John Walker.
+   Bacula® is a registered trademark of Kern Sibbald.
    The licensor of Bacula is the Free Software Foundation Europe
    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
    Switzerland, email:ftf@fsfeurope.org.
 #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 */
-extern int r_first;
-extern int r_last;
-extern struct s_res resources[];
 extern jobq_t job_queue;              /* job queue */
 
 
@@ -99,69 +109,68 @@ 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_("memory"),     memory_cmd,    _("print current memory usage")},
- { NT_("messages"),   messagescmd,   _("messages")},
- { 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>]")},
+struct cmdstruct { const char *key; int (*func)(UAContext *ua, const char *cmd); const char *help; const bool use_in_rs;};
+static struct cmdstruct commands[] = {                                      /* Can use it in Console RunScript*/
+ { NT_("add"),        add_cmd,         _("[pool=<pool-name> storage=<storage> jobid=<JobId>] -- add media to a pool"),                      false},
+ { NT_("autodisplay"), autodisplay_cmd, _("autodisplay [on|off] -- console messages"),false},
+ { NT_("automount"),   automount_cmd,  _("automount [on|off] -- after label"),        false},
+ { NT_("cancel"),     cancel_cmd,    _("cancel [jobid=<number> job=<job-name> ujobid=<unique-jobid>] -- cancel a job"), false},
+ { NT_("create"),     create_cmd,    _("create [pool=<pool-name>] -- create DB Pool from resource"),               false},
+ { NT_("delete"),     delete_cmd,    _("delete [volume=<vol-name> pool=<pool-name> job jobid=<id>]"), true},
+ { NT_("disable"),    disable_cmd,   _("disable <job=name> -- disable a job"),        true},
+ { NT_("enable"),     enable_cmd,    _("enable <job=name> -- enable a job"),          true},
+ { NT_("estimate"),   estimate_cmd,  _("performs FileSet estimate, listing gives full listing"), true},
+ { NT_("exit"),       quit_cmd,      _("exit = quit"),                                false},
+ { NT_("gui"),        gui_cmd,       _("gui [on|off] -- non-interactive gui mode"),   false},
+ { NT_("help"),       help_cmd,      _("print this command"),                         false},
+ { NT_("label"),      label_cmd,     _("label a tape"),                               false},
+ { NT_("list"),       list_cmd,      _("list [pools | jobs | jobtotals | media <pool=pool-name> | files <jobid=nn> | copies <jobid=nn>]; from catalog"), true},
+ { NT_("llist"),      llist_cmd,     _("full or long list like list command"),        true},
+ { NT_("memory"),     memory_cmd,    _("print current memory usage"),                 true},
+ { NT_("messages"),   messagescmd,   _("messages"),                                   false},
+ { NT_("mount"),      mount_cmd,     _("mount storage=<storage-name> [ slot=<num> ] [ drive=<num> ] or mount [ jobid=<id> | job=<job-name> ]"),                       false},
+ { NT_("prune"),      prunecmd,      _("files|jobs|volume client=<client-name> volume=<volume-name> prune expired records from catalog"),         true},
+ { NT_("purge"),      purgecmd,      _("purge records from catalog"),                 true},
+ { NT_("python"),     python_cmd,    _("python control commands"),                    false},
+ { NT_("quit"),       quit_cmd,      _("quit"),                                       false},
+ { NT_("query"),      querycmd,      _("query catalog"),                              false},
+ { NT_("restore"),    restore_cmd,   _("restore files"),                              false},
+ { NT_("relabel"),    relabel_cmd,   _("relabel storage=<storage-name> oldvolume=<old-volume-name> volume=<newvolume-name> -- relabel a tape"),                             false},
+ { NT_("release"),    release_cmd,   _("release <storage-name>"),                     false},
+ { NT_("reload"),     reload_cmd,    _("reload conf file"),                           true},
+ { NT_("run"),        run_cmd,       _("run job=<job-name> client=<client-name> fileset=<FileSet-name> level=<level-keyword> storage=<storage-name> where=<directory-prefix> when=<universal-time-specification> yes"),                             false}, /* need to be check */
+ { NT_("setdebug"),   setdebug_cmd,  _("setdebug level=nn [trace=0/1 client=<client-name> | dir | director | storage=<storage-name> | all]  -- sets debug level"),                           true},
+ { NT_("setip"),      setip_cmd,     _("sets new client address -- if authorized"),   false},
+ { NT_("show"),       show_cmd,      _("show (resource records) [jobs | pools | ... | all]"), true},
+ { NT_("status"),     status_cmd,    _("status [all | dir=<dir-name> | director | client=<client-name> | storage=<storage-name> | days=nnn]"),           true},
+ { NT_("sqlquery"),   sqlquerycmd,   _("use SQL to query catalog"),                   false},
+ { NT_("time"),       time_cmd,      _("print current time"),                         true},
+ { NT_("trace"),      trace_cmd,     _("turn on/off trace to file"),                  true},
+ { NT_("unmount"),    unmount_cmd,   _("unmount storage=<storage-name> [ drive=<num> ] or unmount [ jobid=<id> | job=<job-name> ]"),                     false},
+ { NT_("umount"),     unmount_cmd,   _("umount - for old-time Unix guys, see unmount"),false},
+ { NT_("update"),     update_cmd,    _("update Volume, Pool or slots"),               true},
+ { NT_("use"),        use_cmd,       _("use <database-name> -- catalog xxx"),                            false},
+ { NT_("var"),        var_cmd,       _("does variable expansion"),                    false},
+ { NT_("version"),    version_cmd,   _("print Director version"),                     true},
+ { NT_("wait"),       wait_cmd,      _("wait [<jobname=name> | <jobid=nnn> | <ujobid=complete_name>] -- wait until no jobs are running"), false},
              };
 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
 
 /*
  * Execute a command from the UA
  */
-int do_a_command(UAContext *ua, const char *cmd)
+bool do_a_command(UAContext *ua)
 {
    unsigned int i;
-   int len, stat;
+   int len;
    bool ok = false;
    bool found = false;
    BSOCK *user = ua->UA_sock;
 
-   stat = 1;
 
-   Dmsg1(900, "Command: %s\n", ua->UA_sock->msg);
+   Dmsg1(900, "Command: %s\n", ua->argk[0]);
    if (ua->argc == 0) {
-      return 1;
+      return false;
    }
 
    while (ua->jcr->wstorage->size()) {
@@ -176,14 +185,20 @@ int do_a_command(UAContext *ua, const char *cmd)
              !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, cmd);   /* go execute command */
+         ok = (*commands[i].func)(ua, ua->cmd);   /* go execute command */
          found = true;
          break;
       }
    }
    if (!found) {
-      user->fsend(_("%s: is an invalid command.\n"), ua->argk[0]);
+      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;
@@ -244,12 +259,10 @@ static int add_cmd(UAContext *ua, const char *cmd)
 
    while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
       ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
-      for (;;) {
-         if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
-            return 1;
-         }
-         pr.MaxVols = ua->pint32_val;
+      if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
+         return 1;
       }
+      pr.MaxVols = ua->pint32_val;
    }
 
    /* Get media type */
@@ -277,27 +290,30 @@ static int add_cmd(UAContext *ua, const char *cmd)
       }
       break;
    }
-getVolName:
-   if (num == 0) {
-      if (!get_cmd(ua, _("Enter Volume name: "))) {
-         return 1;
+
+   for (;;) {
+      if (num == 0) {
+         if (!get_cmd(ua, _("Enter Volume name: "))) {
+            return 1;
+         }
+      } else {
+         if (!get_cmd(ua, _("Enter base volume name: "))) {
+            return 1;
+         }
       }
-   } else {
-      if (!get_cmd(ua, _("Enter base volume name: "))) {
-         return 1;
+      /* Don't allow | in Volume name because it is the volume separator character */
+      if (!is_volume_name_legal(ua, ua->cmd)) {
+         continue;
       }
-   }
-   /* Don't allow | in Volume name because it is the volume separator character */
-   if (!is_volume_name_legal(ua, ua->cmd)) {
-      goto getVolName;
-   }
-   if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
-      ua->warning_msg(_("Volume name too long.\n"));
-      goto getVolName;
-   }
-   if (strlen(ua->cmd) == 0) {
-      ua->warning_msg(_("Volume name must be at least one character long.\n"));
-      goto getVolName;
+      if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
+         ua->warning_msg(_("Volume name too long.\n"));
+         continue;
+      }
+      if (strlen(ua->cmd) == 0) {
+         ua->warning_msg(_("Volume name must be at least one character long.\n"));
+         continue;
+      }
+      break;
    }
 
    bstrncpy(name, ua->cmd, sizeof(name));
@@ -478,10 +494,19 @@ static int cancel_cmd(UAContext *ua, const char *cmd)
       if (do_prompt(ua, _("Job"),  _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
          return 1;
       }
-      if (njobs == 1) {
-         if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
+      if (ua->api && njobs == 1) {
+         char nbuf[1000];
+         bsnprintf(nbuf, sizeof(nbuf), _("Cancel: %s\n\n%s"), buf,  
+                   _("Confirm cancel?"));
+         if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
             return 1;
          }
+      } else {
+         if (njobs == 1) {
+            if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
+               return 1;
+            }
+         }
       }
       sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
       jcr = get_jcr_by_full_name(JobName);
@@ -667,6 +692,7 @@ static int create_cmd(UAContext *ua, const char *cmd)
 
 
 extern DIRRES *director;
+extern char *configfile;
 
 /*
  * Python control command
@@ -674,14 +700,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;
 }
 
@@ -781,12 +822,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;
 }
@@ -807,12 +848,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;
 }
@@ -907,9 +948,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;
@@ -1066,33 +1104,55 @@ static int estimate_cmd(UAContext *ua, const char *cmd)
    char since[MAXSTRING];
    JCR *jcr = ua->jcr;
 
-   jcr->JobLevel = L_FULL;
+   jcr->set_JobLevel(L_FULL);
    for (int i=1; i<ua->argc; i++) {
       if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
           strcasecmp(ua->argk[i], NT_("fd")) == 0) {
          if (ua->argv[i]) {
             client = GetClientResWithName(ua->argv[i]);
+            if (!client) {
+               ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
+               return 1;
+            }
             continue;
+         } else {
+            ua->error_msg(_("Client name missing.\n"));
+            return 1;
          }
       }
       if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
          if (ua->argv[i]) {
             job = GetJobResWithName(ua->argv[i]);
-            if (job && !acl_access_ok(ua, Job_ACL, job->name())) {
+            if (!job) {
+               ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
+               return 1;
+            }
+            if (!acl_access_ok(ua, Job_ACL, job->name())) {
                ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
                return 1;
             }
             continue;
+         } else {
+            ua->error_msg(_("Job name missing.\n"));
+            return 1;
          }
+
       }
       if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
          if (ua->argv[i]) {
             fileset = GetFileSetResWithName(ua->argv[i]);
-            if (fileset && !acl_access_ok(ua, FileSet_ACL, fileset->name())) {
+            if (!fileset) {
+               ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
+               return 1;
+            }
+            if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
                ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
                return 1;
             }
             continue;
+         } else {
+            ua->error_msg(_("Fileset name missing.\n"));
+            return 1;
          }
       }
       if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
@@ -1100,10 +1160,15 @@ static int estimate_cmd(UAContext *ua, const char *cmd)
          continue;
       }
       if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
-         if (!get_level_from_name(ua->jcr, ua->argv[i])) {
-            ua->error_msg(_("Level %s not valid.\n"), ua->argv[i]);
+         if (ua->argv[i]) {
+            if (!get_level_from_name(ua->jcr, ua->argv[i])) {
+               ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
+            }
+            continue;
+         } else {
+           ua->error_msg(_("Level value missing.\n"));
+           return 1;
          }
-         continue;
       }
    }
    if (!job && !(client && fileset)) {
@@ -1131,14 +1196,18 @@ static int estimate_cmd(UAContext *ua, const char *cmd)
    jcr->client = client;
    jcr->fileset = fileset;
    close_db(ua);
-   ua->catalog = client->catalog;
+   if (job->pool->catalog) {
+      ua->catalog = job->pool->catalog;
+   } else {
+      ua->catalog = client->catalog;
+   }
 
    if (!open_db(ua)) {
       return 1;
    }
 
    jcr->job = job;
-   jcr->JobType = JT_BACKUP;
+   jcr->set_JobType(JT_BACKUP);
    init_jcr_job_record(jcr);
 
    if (!get_or_create_client_record(jcr)) {
@@ -1151,7 +1220,7 @@ static int estimate_cmd(UAContext *ua, const char *cmd)
    get_level_since_time(ua->jcr, since, sizeof(since));
 
    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
-      job->client->name(), job->client->address, job->client->FDport);
+      jcr->client->name(), jcr->client->address, jcr->client->FDport);
    if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
       ua->error_msg(_("Failed to connect to Client.\n"));
       return 1;
@@ -1364,6 +1433,7 @@ static void do_job_delete(UAContext *ua, JobId_t JobId)
 static int delete_volume(UAContext *ua)
 {
    MEDIA_DBR mr;
+   char buf[1000];
 
    if (!select_media_dbr(ua, &mr)) {
       return 1;
@@ -1372,8 +1442,14 @@ static int delete_volume(UAContext *ua)
       "and all Jobs saved on that volume from the Catalog\n"),
       mr.VolumeName);
 
-   if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
-      return 1;
+   if (find_arg(ua, "yes") >= 0) {
+      ua->pint32_val = 1; /* Have "yes" on command line already" */
+   } else {
+      bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
+         mr.VolumeName);
+      if (!get_yesno(ua, buf)) {
+         return 1;
+      }
    }
    if (ua->pint32_val) {
       db_delete_media_record(ua->jcr, ua->db, &mr);
@@ -1387,13 +1463,16 @@ static int delete_volume(UAContext *ua)
 static int delete_pool(UAContext *ua)
 {
    POOL_DBR  pr;
+   char buf[200];
 
    memset(&pr, 0, sizeof(pr));
 
    if (!get_pool_dbr(ua, &pr)) {
       return 1;
    }
-   if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
+   bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
+      pr.Name);
+   if (!get_yesno(ua, buf)) {
       return 1;
    }
    if (ua->pint32_val) {
@@ -1424,10 +1503,10 @@ static void do_mount_cmd(UAContext *ua, const char *command)
    Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
 
    store.store = get_storage_resource(ua, true/*arg is storage*/);
-   pm_strcpy(store.store_source, _("unknown source"));
    if (!store.store) {
       return;
    }
+   pm_strcpy(store.store_source, _("unknown source"));
    set_wstorage(jcr, &store);
    drive = get_storage_drive(ua, store.store);
    if (strcmp(command, "mount") == 0) {
@@ -1537,8 +1616,11 @@ static int status_handler(void *ctx, int num_fields, char **row)
 int wait_cmd(UAContext *ua, const char *cmd)
 {
    JCR *jcr;
+   int i;
+   time_t stop_time = 0;
 
-   /* no args
+   /*
+    * no args
     * Wait until no job is running
     */
    if (ua->argc == 1) {
@@ -1560,6 +1642,11 @@ int wait_cmd(UAContext *ua, const char *cmd)
       return 1;
    }
 
+   i = find_arg_with_value(ua, NT_("timeout")); 
+   if (i > 0 && ua->argv[i]) {
+      stop_time = time(NULL) + str_to_int64(ua->argv[i]);
+   }
+
    /* we have jobid, jobname or ujobid argument */
 
    uint32_t jobid = 0 ;
@@ -1597,6 +1684,27 @@ int wait_cmd(UAContext *ua, const char *cmd)
             free_jcr(jcr);
          }
          break;
+      /* Wait for a mount request */
+      } else if (strcasecmp(ua->argk[i], "mount") == 0) {
+         for (bool waiting=false; !waiting; ) {
+            foreach_jcr(jcr) {
+               if (jcr->JobId != 0 && 
+                   (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
+                  waiting = true;
+                  break;
+               }
+            }
+            endeach_jcr(jcr);
+            if (waiting) {
+               break;
+            }
+            if (stop_time && (time(NULL) >= stop_time)) {
+               ua->warning_msg(_("Wait on mount timed out\n"));
+               return 1;
+            }
+            bmicrosleep(1, 0);
+         }
+         return 1;
       }
    }
 
@@ -1606,7 +1714,7 @@ int wait_cmd(UAContext *ua, const char *cmd)
    }
 
    /*
-    * We wait the end of job
+    * We wait the end of a specific job
     */
 
    bmicrosleep(0, 200000);            /* let job actually start */
@@ -1695,12 +1803,32 @@ int qhelp_cmd(UAContext *ua, const char *cmd)
    return 1;
 }
 
+#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,
+                NPRTB(director->verid), HOST_OS, DISTNAME, DISTVER);
+   return 1;
+}
+#else
+/*
+ *  Test code -- turned on only for debug testing 
+ */
+static int version_cmd(UAContext *ua, const char *cmd)
+{
+   dbid_list ids;
+   POOL_MEM query(PM_MESSAGE);
+   open_db(ua);
+   Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
+   db_get_query_dbids(ua->jcr, ua->db, query, ids);
+   ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
+   for (int i=0; i < ids.num_ids; i++) {
+      ua->send_msg("id=%d\n", ids.DBId[i]);
+   }
+   close_db(ua);
    return 1;
 }
+#endif
 
 /* 
  * This call explicitly checks for a catalog=xxx and
@@ -1801,7 +1929,8 @@ bool open_db(UAContext *ua)
    ua->jcr->catalog = ua->catalog;
 
    Dmsg0(100, "UA Open database\n");
-   ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
+   ua->db = db_init(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name, 
+                             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);