]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/dird/ua_cmds.c
kes Print the Volume purged message only for real jobs to keep
[bacula/bacula] / bacula / src / dird / ua_cmds.c
index 38a2655e417380531de6300b4c713bb6de4c6b64..481b51b0e3294762be72aa334c42abfb61a77294 100644 (file)
@@ -7,11 +7,11 @@
  *   Version $Id$
  */
 /*
  *   Version $Id$
  */
 /*
-   Copyright (C) 2000-2005 Kern Sibbald
+   Copyright (C) 2000-2006 Kern Sibbald
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
-   version 2 as ammended with additional clauses defined in the
+   version 2 as amended with additional clauses defined in the
    file LICENSE in the main source directory.
 
    This program is distributed in the hope that it will be useful,
    file LICENSE in the main source directory.
 
    This program is distributed in the hope that it will be useful,
@@ -30,7 +30,6 @@
 extern int r_first;
 extern int r_last;
 extern struct s_res resources[];
 extern int r_first;
 extern int r_last;
 extern struct s_res resources[];
-extern char my_name[];
 extern jobq_t job_queue;              /* job queue */
 
 
 extern jobq_t job_queue;              /* job queue */
 
 
@@ -56,6 +55,8 @@ extern int update_cmd(UAContext *ua, const char *cmd);
 static int add_cmd(UAContext *ua, const char *cmd);
 static int create_cmd(UAContext *ua, const char *cmd);
 static int cancel_cmd(UAContext *ua, const char *cmd);
 static int add_cmd(UAContext *ua, const char *cmd);
 static int create_cmd(UAContext *ua, const char *cmd);
 static int cancel_cmd(UAContext *ua, const char *cmd);
+static int enable_cmd(UAContext *ua, const char *cmd);
+static int disable_cmd(UAContext *ua, const char *cmd);
 static int setdebug_cmd(UAContext *ua, const char *cmd);
 static int trace_cmd(UAContext *ua, const char *cmd);
 static int var_cmd(UAContext *ua, const char *cmd);
 static int setdebug_cmd(UAContext *ua, const char *cmd);
 static int trace_cmd(UAContext *ua, const char *cmd);
 static int var_cmd(UAContext *ua, const char *cmd);
@@ -85,45 +86,47 @@ 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[] = {
 
 struct cmdstruct { const char *key; int (*func)(UAContext *ua, const char *cmd); const char *help; };
 static struct cmdstruct commands[] = {
- { N_("add"),        add_cmd,         _("add media to a pool")},
- { N_("autodisplay"), autodisplay_cmd, _("autodisplay [on|off] -- console messages")},
- { N_("automount"),   automount_cmd,  _("automount [on|off] -- after label")},
- { N_("cancel"),     cancel_cmd,    _("cancel [<jobid=nnn> | <job=name>] -- cancel a job")},
- { N_("create"),     create_cmd,    _("create DB Pool from resource")},
- { N_("delete"),     delete_cmd,    _("delete [pool=<pool-name> | media volume=<volume-name>]")},
- { N_("estimate"),   estimate_cmd,  _("performs FileSet estimate, listing gives full listing")},
- { N_("exit"),       quit_cmd,      _("exit = quit")},
- { N_("gui"),        gui_cmd,       _("gui [on|off] -- non-interactive gui mode")},
- { N_("help"),       help_cmd,      _("print this command")},
- { N_("list"),       list_cmd,      _("list [pools | jobs | jobtotals | media <pool=pool-name> | files <jobid=nn>]; from catalog")},
- { N_("label"),      label_cmd,     _("label a tape")},
- { N_("llist"),      llist_cmd,     _("full or long list like list command")},
- { N_("messages"),   messagescmd,   _("messages")},
- { N_("mount"),      mount_cmd,     _("mount <storage-name>")},
- { N_("prune"),      prunecmd,      _("prune expired records from catalog")},
- { N_("purge"),      purgecmd,      _("purge records from catalog")},
- { N_("python"),     python_cmd,    _("python control commands")},
- { N_("quit"),       quit_cmd,      _("quit")},
- { N_("query"),      querycmd,      _("query catalog")},
- { N_("restore"),    restore_cmd,   _("restore files")},
- { N_("relabel"),    relabel_cmd,   _("relabel a tape")},
- { N_("release"),    release_cmd,   _("release <storage-name>")},
- { N_("reload"),     reload_cmd,    _("reload conf file")},
- { N_("run"),        run_cmd,       _("run <job-name>")},
- { N_("status"),     status_cmd,    _("status [storage | client]=<name>")},
- { N_("setdebug"),   setdebug_cmd,  _("sets debug level")},
- { N_("setip"),      setip_cmd,     _("sets new client address -- if authorized")},
- { N_("show"),       show_cmd,      _("show (resource records) [jobs | pools | ... | all]")},
- { N_("sqlquery"),   sqlquerycmd,   _("use SQL to query catalog")},
- { N_("time"),       time_cmd,      _("print current time")},
- { N_("trace"),      trace_cmd,     _("turn on/off trace to file")},
- { N_("unmount"),    unmount_cmd,   _("unmount <storage-name>")},
- { N_("umount"),     unmount_cmd,   _("umount <storage-name> for old-time Unix guys")},
- { N_("update"),     update_cmd,    _("update Volume, Pool or slots")},
- { N_("use"),        use_cmd,       _("use catalog xxx")},
- { N_("var"),        var_cmd,       _("does variable expansion")},
- { N_("version"),    version_cmd,   _("print Director version")},
- { N_("wait"),       wait_cmd,      _("wait until no jobs are running")},
+ { 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_("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))
 
              };
 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
 
@@ -143,10 +146,16 @@ int do_a_command(UAContext *ua, const char *cmd)
       return 1;
    }
 
       return 1;
    }
 
+   while (ua->jcr->wstorage->size()) {
+      ua->jcr->wstorage->remove(0);
+   }
+
    len = strlen(ua->argk[0]);
    for (i=0; i<comsize; i++) {     /* search for command */
    len = strlen(ua->argk[0]);
    for (i=0; i<comsize; i++) {     /* search for command */
-      if (strncasecmp(ua->argk[0],  _(commands[i].key), len) == 0) {
-         if (!acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
+      if (strncasecmp(ua->argk[0],  commands[i].key, len) == 0) {
+         /* Check if command permitted, but "quit" is always OK */
+         if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
+             !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
             break;
          }
          stat = (*commands[i].func)(ua, cmd);   /* go execute command */
             break;
          }
          stat = (*commands[i].func)(ua, cmd);   /* go execute command */
@@ -168,7 +177,7 @@ int do_a_command(UAContext *ua, const char *cmd)
 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
 {
    mr->PoolId = pr->PoolId;
 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
 {
    mr->PoolId = pr->PoolId;
-   bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus));
+   bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
    mr->Recycle = pr->Recycle;
    mr->VolRetention = pr->VolRetention;
    mr->VolUseDuration = pr->VolUseDuration;
    mr->Recycle = pr->Recycle;
    mr->VolRetention = pr->VolRetention;
    mr->VolUseDuration = pr->VolUseDuration;
@@ -176,6 +185,7 @@ void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
    mr->MaxVolFiles = pr->MaxVolFiles;
    mr->MaxVolBytes = pr->MaxVolBytes;
    mr->LabelType = pr->LabelType;
    mr->MaxVolFiles = pr->MaxVolFiles;
    mr->MaxVolBytes = pr->MaxVolBytes;
    mr->LabelType = pr->LabelType;
+   mr->Enabled = 1;
 }
 
 
 }
 
 
@@ -212,7 +222,7 @@ static int add_cmd(UAContext *ua, const char *cmd)
       pr.MaxVols, pr.PoolType);
 
    while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
       pr.MaxVols, pr.PoolType);
 
    while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
-      bsendmsg(ua, _("Pool already has maximum volumes = %d\n"), pr.MaxVols);
+      bsendmsg(ua, _("Pool already has maximum volumes=%d\n"), pr.MaxVols);
       for (;;) {
          if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
             return 1;
       for (;;) {
          if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
             return 1;
@@ -222,7 +232,7 @@ static int add_cmd(UAContext *ua, const char *cmd)
    }
 
    /* Get media type */
    }
 
    /* Get media type */
-   if ((store = get_storage_resource(ua, 0)) != NULL) {
+   if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
       bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
    } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
       return 1;
       bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
    } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
       return 1;
@@ -306,6 +316,7 @@ getVolName:
       mr.Slot = Slot++;
       mr.InChanger = InChanger;
       mr.StorageId = store->StorageId;
       mr.Slot = Slot++;
       mr.InChanger = InChanger;
       mr.StorageId = store->StorageId;
+      mr.Enabled = 1;
       Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
       if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
          bsendmsg(ua, "%s", db_strerror(ua->db));
       Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
       if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
          bsendmsg(ua, "%s", db_strerror(ua->db));
@@ -345,7 +356,7 @@ int automount_cmd(UAContext *ua, const char *cmd)
       onoff = ua->argk[1];
    }
 
       onoff = ua->argk[1];
    }
 
-   ua->automount = (strcasecmp(onoff, _("off")) == 0) ? 0 : 1;
+   ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
    return 1;
 }
 
    return 1;
 }
 
@@ -365,27 +376,39 @@ static int cancel_cmd(UAContext *ua, const char *cmd)
    }
 
    for (i=1; i<ua->argc; i++) {
    }
 
    for (i=1; i<ua->argc; i++) {
-      if (strcasecmp(ua->argk[i], _("jobid")) == 0) {
+      if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
          uint32_t JobId;
          if (!ua->argv[i]) {
             break;
          }
          JobId = str_to_int64(ua->argv[i]);
          if (!(jcr=get_jcr_by_id(JobId))) {
          uint32_t JobId;
          if (!ua->argv[i]) {
             break;
          }
          JobId = str_to_int64(ua->argv[i]);
          if (!(jcr=get_jcr_by_id(JobId))) {
-            bsendmsg(ua, _("JobId %d is not running.\n"),  JobId);
+            bsendmsg(ua, _("JobId %s is not running. Use Job name to cancel inactive jobs.\n"),  ua->argv[i]);
             return 1;
          }
          break;
             return 1;
          }
          break;
-      } else if (strcasecmp(ua->argk[i], _("job")) == 0) {
+      } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
          if (!ua->argv[i]) {
             break;
          }
          if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
          if (!ua->argv[i]) {
             break;
          }
          if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
-            bsendmsg(ua, _("Job %s is not running.\n"), ua->argv[i]);
-            return 1;
+            bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
+            jcr = new_jcr(sizeof(JCR), dird_free_jcr);
+            bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
+         }
+         break;
+      } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
+         if (!ua->argv[i]) {
+            break;
+         }
+         if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
+            bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
+            jcr = new_jcr(sizeof(JCR), dird_free_jcr);
+            bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
          }
          break;
       }
          }
          break;
       }
+
    }
    /* If we still do not have a jcr,
     *   throw up a list and ask the user to select one.
    }
    /* If we still do not have a jcr,
     *   throw up a list and ask the user to select one.
@@ -395,12 +418,11 @@ static int cancel_cmd(UAContext *ua, const char *cmd)
       /* Count Jobs running */
       foreach_jcr(jcr) {
          if (jcr->JobId == 0) {      /* this is us */
       /* Count Jobs running */
       foreach_jcr(jcr) {
          if (jcr->JobId == 0) {      /* this is us */
-            free_jcr(jcr);
             continue;
          }
             continue;
          }
-         free_jcr(jcr);
          njobs++;
       }
          njobs++;
       }
+      endeach_jcr(jcr);
 
       if (njobs == 0) {
          bsendmsg(ua, _("No Jobs running.\n"));
 
       if (njobs == 0) {
          bsendmsg(ua, _("No Jobs running.\n"));
@@ -408,14 +430,14 @@ static int cancel_cmd(UAContext *ua, const char *cmd)
       }
       start_prompt(ua, _("Select Job:\n"));
       foreach_jcr(jcr) {
       }
       start_prompt(ua, _("Select Job:\n"));
       foreach_jcr(jcr) {
+         char ed1[50];
          if (jcr->JobId == 0) {      /* this is us */
          if (jcr->JobId == 0) {      /* this is us */
-            free_jcr(jcr);
             continue;
          }
             continue;
          }
-         bsnprintf(buf, sizeof(buf), "JobId=%d Job=%s", jcr->JobId, jcr->Job);
+         bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
          add_prompt(ua, buf);
          add_prompt(ua, buf);
-         free_jcr(jcr);
       }
       }
+      endeach_jcr(jcr);
 
       if (do_prompt(ua, _("Job"),  _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
          return 1;
 
       if (do_prompt(ua, _("Job"),  _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
          return 1;
@@ -425,7 +447,6 @@ static int cancel_cmd(UAContext *ua, const char *cmd)
             return 1;
          }
       }
             return 1;
          }
       }
-      /* NOTE! This increments the ref_count */
       sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
       jcr = get_jcr_by_full_name(JobName);
       if (!jcr) {
       sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
       jcr = get_jcr_by_full_name(JobName);
       if (!jcr) {
@@ -436,7 +457,6 @@ static int cancel_cmd(UAContext *ua, const char *cmd)
 
    ret = cancel_job(ua, jcr);
    free_jcr(jcr);
 
    ret = cancel_job(ua, jcr);
    free_jcr(jcr);
-
    return ret;
 }
 
    return ret;
 }
 
@@ -464,7 +484,6 @@ void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
    pr->LabelType = pool->LabelType;
    pr->UseOnce = pool->use_volume_once;
    pr->UseCatalog = pool->use_catalog;
    pr->LabelType = pool->LabelType;
    pr->UseOnce = pool->use_volume_once;
    pr->UseCatalog = pool->use_catalog;
-   pr->AcceptAnyVolume = pool->accept_any_volume;
    pr->Recycle = pool->Recycle;
    pr->VolRetention = pool->VolRetention;
    pr->VolUseDuration = pool->VolUseDuration;
    pr->Recycle = pool->Recycle;
    pr->VolRetention = pool->VolRetention;
    pr->VolUseDuration = pool->VolUseDuration;
@@ -559,7 +578,7 @@ extern DIRRES *director;
  */
 static int python_cmd(UAContext *ua, const char *cmd)
 {
  */
 static int python_cmd(UAContext *ua, const char *cmd)
 {
-   if (ua->argc >= 1 && strcasecmp(ua->argk[1], _("restart")) == 0) {
+   if (ua->argc >= 1 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
       term_python_interpreter();
       init_python_interpreter(director->hdr.name, 
          director->scripts_directory, "DirStartUp");
       term_python_interpreter();
       init_python_interpreter(director->hdr.name, 
          director->scripts_directory, "DirStartUp");
@@ -605,15 +624,58 @@ get_out:
 }
 
 
 }
 
 
+static void do_en_disable_cmd(UAContext *ua, bool setting)
+{
+   JOB *job;
+   int i;
+
+   i = find_arg_with_value(ua, NT_("job")); 
+   if (i < 0) { 
+      job = select_job_resource(ua);
+      if (!job) {
+         return;
+      }
+   } else {
+      LockRes();
+      job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
+      UnlockRes();
+   } 
+   if (!job) {
+      bsendmsg(ua, _("Job \"%s\" not found.\n"), ua->argv[i]);
+      return;
+   }
+
+   if (!acl_access_ok(ua, Job_ACL, job->hdr.name)) {
+      bsendmsg(ua, _("Illegal command from this console.\n"));
+      return;
+   }
+   job->enabled = setting;
+   bsendmsg(ua, _("Job \"%s\" %sabled\n"), job->hdr.name, setting?"en":"dis");
+   return;
+}
+
+static int enable_cmd(UAContext *ua, const char *cmd)
+{
+   do_en_disable_cmd(ua, true);
+   return 1;
+}
+
+static int disable_cmd(UAContext *ua, const char *cmd)
+{
+   do_en_disable_cmd(ua, false);
+   return 1;
+}
+
+
 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
 {
    BSOCK *sd;
    JCR *jcr = ua->jcr;
 
 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
 {
    BSOCK *sd;
    JCR *jcr = ua->jcr;
 
-   set_storage(jcr, store);
+   set_wstorage(jcr, store);
    /* Try connecting for up to 15 seconds */
    bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
    /* Try connecting for up to 15 seconds */
    bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
-      store->hdr.name, store->address, store->SDport);
+      store->name(), store->address, store->SDport);
    if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
       bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
       return;
    if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
       bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
       return;
@@ -752,7 +814,7 @@ static int setdebug_cmd(UAContext *ua, const char *cmd)
    Dmsg1(120, "setdebug:%s:\n", cmd);
 
    level = -1;
    Dmsg1(120, "setdebug:%s:\n", cmd);
 
    level = -1;
-   i = find_arg_with_value(ua, _("level"));
+   i = find_arg_with_value(ua, "level");
    if (i >= 0) {
       level = atoi(ua->argv[i]);
    }
    if (i >= 0) {
       level = atoi(ua->argv[i]);
    }
@@ -764,7 +826,7 @@ static int setdebug_cmd(UAContext *ua, const char *cmd)
    }
 
    /* Look for trace flag. -1 => not change */
    }
 
    /* Look for trace flag. -1 => not change */
-   i = find_arg_with_value(ua, _("trace"));
+   i = find_arg_with_value(ua, "trace");
    if (i >= 0) {
       trace_flag = atoi(ua->argv[i]);
       if (trace_flag > 0) {
    if (i >= 0) {
       trace_flag = atoi(ua->argv[i]);
       if (trace_flag > 0) {
@@ -774,18 +836,18 @@ static int setdebug_cmd(UAContext *ua, const char *cmd)
 
    /* General debug? */
    for (i=1; i<ua->argc; i++) {
 
    /* General debug? */
    for (i=1; i<ua->argc; i++) {
-      if (strcasecmp(ua->argk[i], _("all")) == 0) {
+      if (strcasecmp(ua->argk[i], "all") == 0) {
          do_all_setdebug(ua, level, trace_flag);
          return 1;
       }
          do_all_setdebug(ua, level, trace_flag);
          return 1;
       }
-      if (strcasecmp(ua->argk[i], _("dir")) == 0 ||
-          strcasecmp(ua->argk[i], _("director")) == 0) {
+      if (strcasecmp(ua->argk[i], "dir") == 0 ||
+          strcasecmp(ua->argk[i], "director") == 0) {
          debug_level = level;
          set_trace(trace_flag);
          return 1;
       }
          debug_level = level;
          set_trace(trace_flag);
          return 1;
       }
-      if (strcasecmp(ua->argk[i], _("client")) == 0 ||
-          strcasecmp(ua->argk[i], _("fd")) == 0) {
+      if (strcasecmp(ua->argk[i], "client") == 0 ||
+          strcasecmp(ua->argk[i], "fd") == 0) {
          client = NULL;
          if (ua->argv[i]) {
             client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
          client = NULL;
          if (ua->argv[i]) {
             client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
@@ -801,9 +863,9 @@ static int setdebug_cmd(UAContext *ua, const char *cmd)
          }
       }
 
          }
       }
 
-      if (strcasecmp(ua->argk[i], _("store")) == 0 ||
-          strcasecmp(ua->argk[i], _("storage")) == 0 ||
-          strcasecmp(ua->argk[i], _("sd")) == 0) {
+      if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
+          strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
+          strcasecmp(ua->argk[i], NT_("sd")) == 0) {
          store = NULL;
          if (ua->argv[i]) {
             store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
          store = NULL;
          if (ua->argv[i]) {
             store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
@@ -812,7 +874,7 @@ static int setdebug_cmd(UAContext *ua, const char *cmd)
                return 1;
             }
          }
                return 1;
             }
          }
-         store = get_storage_resource(ua, 0);
+         store = get_storage_resource(ua, false/*no default*/);
          if (store) {
             do_storage_setdebug(ua, store, level, trace_flag);
             return 1;
          if (store) {
             do_storage_setdebug(ua, store, level, trace_flag);
             return 1;
@@ -834,7 +896,7 @@ static int setdebug_cmd(UAContext *ua, const char *cmd)
       set_trace(trace_flag);
       break;
    case 1:
       set_trace(trace_flag);
       break;
    case 1:
-      store = get_storage_resource(ua, 0);
+      store = get_storage_resource(ua, false/*no default*/);
       if (store) {
          do_storage_setdebug(ua, store, level, trace_flag);
       }
       if (store) {
          do_storage_setdebug(ua, store, level, trace_flag);
       }
@@ -870,7 +932,7 @@ static int trace_cmd(UAContext *ua, const char *cmd)
       onoff = ua->argk[1];
    }
 
       onoff = ua->argk[1];
    }
 
-   set_trace((strcasecmp(onoff, _("off")) == 0) ? false : true);
+   set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
    return 1;
 
 }
    return 1;
 
 }
@@ -901,37 +963,36 @@ static int estimate_cmd(UAContext *ua, const char *cmd)
    JOB *job = NULL;
    CLIENT *client = NULL;
    FILESET *fileset = NULL;
    JOB *job = NULL;
    CLIENT *client = NULL;
    FILESET *fileset = NULL;
-   FILESET_DBR fsr;
    int listing = 0;
    char since[MAXSTRING];
    JCR *jcr = ua->jcr;
 
    jcr->JobLevel = L_FULL;
    for (int i=1; i<ua->argc; i++) {
    int listing = 0;
    char since[MAXSTRING];
    JCR *jcr = ua->jcr;
 
    jcr->JobLevel = L_FULL;
    for (int i=1; i<ua->argc; i++) {
-      if (strcasecmp(ua->argk[i], _("client")) == 0 ||
-          strcasecmp(ua->argk[i], _("fd")) == 0) {
+      if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
+          strcasecmp(ua->argk[i], NT_("fd")) == 0) {
          if (ua->argv[i]) {
             client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
             continue;
          }
       }
          if (ua->argv[i]) {
             client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
             continue;
          }
       }
-      if (strcasecmp(ua->argk[i], _("job")) == 0) {
+      if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
          if (ua->argv[i]) {
             job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
             continue;
          }
       }
          if (ua->argv[i]) {
             job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
             continue;
          }
       }
-      if (strcasecmp(ua->argk[i], _("fileset")) == 0) {
+      if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
          if (ua->argv[i]) {
             fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
             continue;
          }
       }
          if (ua->argv[i]) {
             fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
             continue;
          }
       }
-      if (strcasecmp(ua->argk[i], _("listing")) == 0) {
+      if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
          listing = 1;
          continue;
       }
          listing = 1;
          continue;
       }
-      if (strcasecmp(ua->argk[i], _("level")) == 0) {
+      if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
          if (!get_level_from_name(ua->jcr, ua->argv[i])) {
             bsendmsg(ua, _("Level %s not valid.\n"), ua->argv[i]);
          }
          if (!get_level_from_name(ua->jcr, ua->argv[i])) {
             bsendmsg(ua, _("Level %s not valid.\n"), ua->argv[i]);
          }
@@ -972,7 +1033,7 @@ static int estimate_cmd(UAContext *ua, const char *cmd)
    if (!get_or_create_client_record(jcr)) {
       return 1;
    }
    if (!get_or_create_client_record(jcr)) {
       return 1;
    }
-   if (!get_or_create_fileset_record(jcr, &fsr)) {
+   if (!get_or_create_fileset_record(jcr)) {
       return 1;
    }
 
       return 1;
    }
 
@@ -1022,7 +1083,7 @@ static int time_cmd(UAContext *ua, const char *cmd)
    char sdt[50];
    time_t ttime = time(NULL);
    struct tm tm;
    char sdt[50];
    time_t ttime = time(NULL);
    struct tm tm;
-   localtime_r(&ttime, &tm);
+   (void)localtime_r(&ttime, &tm);
    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
    bsendmsg(ua, "%s\n", sdt);
    return 1;
    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
    bsendmsg(ua, "%s\n", sdt);
    return 1;
@@ -1049,9 +1110,9 @@ static int reload_cmd(UAContext *ua, const char *cmd)
 static int delete_cmd(UAContext *ua, const char *cmd)
 {
    static const char *keywords[] = {
 static int delete_cmd(UAContext *ua, const char *cmd)
 {
    static const char *keywords[] = {
-      N_("volume"),
-      N_("pool"),
-      N_("jobid"),
+      NT_("volume"),
+      NT_("pool"),
+      NT_("jobid"),
       NULL};
 
    if (!open_db(ua)) {
       NULL};
 
    if (!open_db(ua)) {
@@ -1067,7 +1128,7 @@ static int delete_cmd(UAContext *ua, const char *cmd)
       return 1;
    case 2:
       int i;
       return 1;
    case 2:
       int i;
-      while ((i=find_arg(ua, _("jobid"))) > 0) {
+      while ((i=find_arg(ua, "jobid")) > 0) {
          delete_job(ua);
          *ua->argk[i] = 0;         /* zap keyword already visited */
       }
          delete_job(ua);
          *ua->argk[i] = 0;         /* zap keyword already visited */
       }
@@ -1111,7 +1172,7 @@ static void delete_job(UAContext *ua)
    JobId_t JobId;
    char *s,*sep,*tok;
 
    JobId_t JobId;
    char *s,*sep,*tok;
 
-   int i = find_arg_with_value(ua, _("jobid"));
+   int i = find_arg_with_value(ua, NT_("jobid"));
    if (i >= 0) {
       if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
         s = bstrdup(ua->argv[i]);
    if (i >= 0) {
       if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
         s = bstrdup(ua->argv[i]);
@@ -1176,21 +1237,15 @@ static void delete_job_id_range(UAContext *ua, char *tok)
 
 /*
  * do_job_delete now performs the actual delete operation atomically
 
 /*
  * do_job_delete now performs the actual delete operation atomically
- * we always return 1 because C++ is pissy about void functions
  */
 
 static void do_job_delete(UAContext *ua, JobId_t JobId)
 {
  */
 
 static void do_job_delete(UAContext *ua, JobId_t JobId)
 {
-   POOLMEM *query = get_pool_memory(PM_MESSAGE);
+   POOL_MEM query(PM_MESSAGE);
    char ed1[50];
 
    char ed1[50];
 
-   Mmsg(query, "DELETE FROM Job WHERE JobId=%s", edit_int64(JobId, ed1));
-   db_sql_query(ua->db, query, NULL, (void *)NULL);
-   Mmsg(query, "DELETE FROM File WHERE JobId=%s", edit_int64(JobId, ed1));
-   db_sql_query(ua->db, query, NULL, (void *)NULL);
-   Mmsg(query, "DELETE FROM JobMedia WHERE JobId=%s", edit_int64(JobId, ed1));
-   db_sql_query(ua->db, query, NULL, (void *)NULL);
-   free_pool_memory(query);
+   purge_files_from_job(ua, JobId);
+   purge_job_from_catalog(ua, JobId);
    bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
 }
 
    bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
 }
 
@@ -1245,21 +1300,27 @@ static void do_mount_cmd(UAContext *ua, const char *command)
    BSOCK *sd;
    JCR *jcr = ua->jcr;
    char dev_name[MAX_NAME_LENGTH];
    BSOCK *sd;
    JCR *jcr = ua->jcr;
    char dev_name[MAX_NAME_LENGTH];
+   int drive;
+   int slot = -1;
 
    if (!open_db(ua)) {
       return;
    }
    Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
 
 
    if (!open_db(ua)) {
       return;
    }
    Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
 
-   store = get_storage_resource(ua, 1);
+   store = get_storage_resource(ua, true/*arg is storage*/);
    if (!store) {
       return;
    }
    if (!store) {
       return;
    }
+   set_wstorage(jcr, store);
+   drive = get_storage_drive(ua, store);
+   if (strcmp(command, "mount") == 0) {
+      slot = get_storage_slot(ua, store);
+   }
 
 
-   Dmsg2(120, "Found storage, MediaType=%s DevName=%s\n",
-      store->media_type, store->dev_name());
+   Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
+      store->media_type, store->dev_name(), drive);
 
 
-   set_storage(jcr, store);
    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
       bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
       return;
    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
       bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
       return;
@@ -1267,7 +1328,11 @@ static void do_mount_cmd(UAContext *ua, const char *command)
    sd = jcr->store_bsock;
    bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
    bash_spaces(dev_name);
    sd = jcr->store_bsock;
    bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
    bash_spaces(dev_name);
-   bnet_fsend(sd, "%s %s", command, dev_name);
+   if (slot > 0) {
+      bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
+   } else {
+      bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
+   }
    while (bnet_recv(sd) >= 0) {
       bsendmsg(ua, "%s", sd->msg);
    }
    while (bnet_recv(sd) >= 0) {
       bsendmsg(ua, "%s", sd->msg);
    }
@@ -1277,7 +1342,7 @@ static void do_mount_cmd(UAContext *ua, const char *command)
 }
 
 /*
 }
 
 /*
- * mount [storage | device] <name>
+ * mount [storage=<name>] [drive=nn] [slot=mm]
  */
 static int mount_cmd(UAContext *ua, const char *cmd)
 {
  */
 static int mount_cmd(UAContext *ua, const char *cmd)
 {
@@ -1287,7 +1352,7 @@ static int mount_cmd(UAContext *ua, const char *cmd)
 
 
 /*
 
 
 /*
- * unmount [storage | device] <name>
+ * unmount [storage=<name>] [drive=nn]
  */
 static int unmount_cmd(UAContext *ua, const char *cmd)
 {
  */
 static int unmount_cmd(UAContext *ua, const char *cmd)
 {
@@ -1297,7 +1362,7 @@ static int unmount_cmd(UAContext *ua, const char *cmd)
 
 
 /*
 
 
 /*
- * release [storage | device] <name>
+ * release [storage=<name>] [drive=nn]
  */
 static int release_cmd(UAContext *ua, const char *cmd)
 {
  */
 static int release_cmd(UAContext *ua, const char *cmd)
 {
@@ -1336,27 +1401,158 @@ int quit_cmd(UAContext *ua, const char *cmd)
    return 1;
 }
 
    return 1;
 }
 
+/* Handler to get job status */
+static int status_handler(void *ctx, int num_fields, char **row)
+{
+   char *val = (char *)ctx;
+
+   if (row[0]) {
+      *val = row[0][0];
+   } else {
+      *val = '?';               /* Unknown by default */
+   }
+
+   return 0;
+}
+
 /*
  * Wait until no job is running
  */
 int wait_cmd(UAContext *ua, const char *cmd)
 {
    JCR *jcr;
 /*
  * Wait until no job is running
  */
 int wait_cmd(UAContext *ua, const char *cmd)
 {
    JCR *jcr;
-   bmicrosleep(0, 200000);            /* let job actually start */
-   for (bool running=true; running; ) {
-      running = false;
-      foreach_jcr(jcr) {
-         if (jcr->JobId != 0) {
-            running = true;
+
+   /* no args
+    * Wait until no job is running
+    */
+   if (ua->argc == 1) {
+      bmicrosleep(0, 200000);            /* let job actually start */
+      for (bool running=true; running; ) {
+         running = false;
+         foreach_jcr(jcr) {
+            if (jcr->JobId != 0) {
+               running = true;
+               break;
+            }
+         }
+         endeach_jcr(jcr);
+
+         if (running) {
+            bmicrosleep(1, 0);
+         }
+      }
+      return 1;
+   }
+
+   /* we have jobid, jobname or ujobid argument */
+
+   uint32_t jobid = 0 ;
+
+   if (!open_db(ua)) {
+      bsendmsg(ua, _("ERR: Can't open db\n")) ;
+      return 1;
+   }
+
+   for (int i=1; i<ua->argc; i++) {
+      if (strcasecmp(ua->argk[i], "jobid") == 0) {
+         if (!ua->argv[i]) {
+            break;
+         }
+         jobid = str_to_int64(ua->argv[i]);
+         break;
+      } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
+                 strcasecmp(ua->argk[i], "job") == 0) {
+         if (!ua->argv[i]) {
+            break;
+         }
+         jcr=get_jcr_by_partial_name(ua->argv[i]) ;
+         if (jcr) {
+            jobid = jcr->JobId ;
             free_jcr(jcr);
             free_jcr(jcr);
+         }
+         break;
+      } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
+         if (!ua->argv[i]) {
             break;
          }
             break;
          }
+         jcr=get_jcr_by_full_name(ua->argv[i]) ;
+         if (jcr) {
+            jobid = jcr->JobId ;
+            free_jcr(jcr);
+         }
+         break;
+      }
+   }
+
+   if (jobid == 0) {
+      bsendmsg(ua, _("ERR: Job was not found\n"));
+      return 1 ;
+   }
+
+   /*
+    * We wait the end of job
+    */
+
+   bmicrosleep(0, 200000);            /* let job actually start */
+   for (bool running=true; running; ) {
+      running = false;
+
+      jcr=get_jcr_by_id(jobid) ;
+
+      if (jcr) {
+         running = true ;
          free_jcr(jcr);
       }
          free_jcr(jcr);
       }
+
       if (running) {
          bmicrosleep(1, 0);
       }
    }
       if (running) {
          bmicrosleep(1, 0);
       }
    }
+
+   /*
+    * We have to get JobStatus
+    */
+
+   int status ;
+   char jobstatus = '?';        /* Unknown by default */
+   char buf[256] ;
+
+   bsnprintf(buf, sizeof(buf),
+             "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
+
+
+   db_sql_query(ua->db, buf,
+                status_handler, (void *)&jobstatus);
+
+   switch (jobstatus) {
+   case JS_Error:
+      status = 1 ;         /* Warning */
+      break;
+
+   case JS_FatalError:
+   case JS_ErrorTerminated:
+   case JS_Canceled:
+      status = 2 ;         /* Critical */
+      break;
+
+   case JS_Terminated:
+      status = 0 ;         /* Ok */
+      break;
+
+   default:
+      status = 3 ;         /* Unknown */
+      break;
+   }
+
+   bsendmsg(ua, "JobId=%i\n", jobid) ;
+   bsendmsg(ua, "JobStatus=%s (%c)\n", 
+            job_status_to_str(jobstatus), 
+            jobstatus) ;
+
+   if (ua->gui) {
+      bsendmsg(ua, "ExitStatus=%i\n", status) ;
+   }
+
    return 1;
 }
 
    return 1;
 }
 
@@ -1378,37 +1574,31 @@ int qhelp_cmd(UAContext *ua, const char *cmd)
    unsigned int i;
 
    for (i=0; i<comsize; i++) {
    unsigned int i;
 
    for (i=0; i<comsize; i++) {
-      bsendmsg(ua, _("%s %s\n"), _(commands[i].key), _(commands[i].help));
+      bsendmsg(ua, "%s %s\n", commands[i].key, _(commands[i].help));
    }
    return 1;
 }
 
 static int version_cmd(UAContext *ua, const char *cmd)
 {
    }
    return 1;
 }
 
 static int version_cmd(UAContext *ua, const char *cmd)
 {
-   bsendmsg(ua, "%s Version: " VERSION " (" BDATE ")\n", my_name);
+   bsendmsg(ua, _("%s Version: %s (%s)\n"), my_name, VERSION, BDATE);
    return 1;
 }
 
 
    return 1;
 }
 
 
-/* A bit brain damaged in that if the user has not done
- * a "use catalog xxx" command, we simply find the first
- * catalog resource and open it.
+/*
+ * Open the catalog database.
  */
  */
-int open_db(UAContext *ua)
+bool open_db(UAContext *ua)
 {
    if (ua->db) {
 {
    if (ua->db) {
-      return 1;
+      return true;
    }
    if (!ua->catalog) {
    }
    if (!ua->catalog) {
-      LockRes();
-      ua->catalog = (CAT *)GetNextRes(R_CATALOG, NULL);
-      UnlockRes();
+      ua->catalog = get_catalog_resource(ua);
       if (!ua->catalog) {
          bsendmsg(ua, _("Could not find a Catalog resource\n"));
       if (!ua->catalog) {
          bsendmsg(ua, _("Could not find a Catalog resource\n"));
-         return 0;
-      } else {
-         bsendmsg(ua, _("Using default Catalog name=%s DB=%s\n"),
-            ua->catalog->hdr.name, ua->catalog->db_name);
+         return false;
       }
    }
 
       }
    }
 
@@ -1426,11 +1616,11 @@ int open_db(UAContext *ua)
          bsendmsg(ua, "%s", db_strerror(ua->db));
       }
       close_db(ua);
          bsendmsg(ua, "%s", db_strerror(ua->db));
       }
       close_db(ua);
-      return 0;
+      return false;
    }
    ua->jcr->db = ua->db;
    Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
    }
    ua->jcr->db = ua->db;
    Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
-   return 1;
+   return true;
 }
 
 void close_db(UAContext *ua)
 }
 
 void close_db(UAContext *ua)