]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_cmds.c
Backport of class based catalog backends into Branch-5.1.
[bacula/bacula] / bacula / src / dird / ua_cmds.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   Bacula Director -- User Agent Commands
31  *
32  *     Kern Sibbald, September MM
33  *
34  *   Version $Id$
35  */
36  
37 #include "bacula.h"
38 #include "dird.h"
39
40 #ifdef HAVE_PYTHON
41
42 #undef _POSIX_C_SOURCE
43 #include <Python.h>
44
45 #include "lib/pythonlib.h"
46
47 /* Imported Functions */
48 extern PyObject *job_getattr(PyObject *self, char *attrname);
49 extern int job_setattr(PyObject *self, char *attrname, PyObject *value);
50
51 #endif /* HAVE_PYTHON */
52
53 /* Imported subroutines */
54
55 /* Imported variables */
56 extern jobq_t job_queue;              /* job queue */
57
58
59 /* Imported functions */
60 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
61 extern int gui_cmd(UAContext *ua, const char *cmd);
62 extern int label_cmd(UAContext *ua, const char *cmd);
63 extern int list_cmd(UAContext *ua, const char *cmd);
64 extern int llist_cmd(UAContext *ua, const char *cmd);
65 extern int messagescmd(UAContext *ua, const char *cmd);
66 extern int prunecmd(UAContext *ua, const char *cmd);
67 extern int purgecmd(UAContext *ua, const char *cmd);
68 extern int querycmd(UAContext *ua, const char *cmd);
69 extern int relabel_cmd(UAContext *ua, const char *cmd);
70 extern int restore_cmd(UAContext *ua, const char *cmd);
71 extern int retentioncmd(UAContext *ua, const char *cmd);
72 extern int show_cmd(UAContext *ua, const char *cmd);
73 extern int sqlquerycmd(UAContext *ua, const char *cmd);
74 extern int status_cmd(UAContext *ua, const char *cmd);
75 extern int update_cmd(UAContext *ua, const char *cmd);
76
77 /* Forward referenced functions */
78 static int add_cmd(UAContext *ua, const char *cmd);
79 static int automount_cmd(UAContext *ua, const char *cmd);
80 static int cancel_cmd(UAContext *ua, const char *cmd);
81 static int create_cmd(UAContext *ua, const char *cmd);
82 static int delete_cmd(UAContext *ua, const char *cmd);
83 static int disable_cmd(UAContext *ua, const char *cmd);
84 static int enable_cmd(UAContext *ua, const char *cmd);
85 static int estimate_cmd(UAContext *ua, const char *cmd);
86 static int help_cmd(UAContext *ua, const char *cmd);
87 static int memory_cmd(UAContext *ua, const char *cmd);
88 static int mount_cmd(UAContext *ua, const char *cmd);
89 static int python_cmd(UAContext *ua, const char *cmd);
90 static int release_cmd(UAContext *ua, const char *cmd);
91 static int reload_cmd(UAContext *ua, const char *cmd);
92 static int setdebug_cmd(UAContext *ua, const char *cmd);
93 static int setip_cmd(UAContext *ua, const char *cmd);
94 static int time_cmd(UAContext *ua, const char *cmd);
95 static int trace_cmd(UAContext *ua, const char *cmd);
96 static int unmount_cmd(UAContext *ua, const char *cmd);
97 static int use_cmd(UAContext *ua, const char *cmd);
98 static int var_cmd(UAContext *ua, const char *cmd);
99 static int version_cmd(UAContext *ua, const char *cmd);
100 static int wait_cmd(UAContext *ua, const char *cmd);
101
102 static void do_job_delete(UAContext *ua, JobId_t JobId);
103 static bool delete_job_id_range(UAContext *ua, char *tok);
104 static int delete_volume(UAContext *ua);
105 static int delete_pool(UAContext *ua);
106 static void delete_job(UAContext *ua);
107
108 int qhelp_cmd(UAContext *ua, const char *cmd);
109 int quit_cmd(UAContext *ua, const char *cmd);
110
111 /* not all in alphabetical order.  New commands are added after existing commands with similar letters
112    to prevent breakage of existing user scripts.  */
113 struct cmdstruct { 
114    const char *key;                             /* command */
115    int (*func)(UAContext *ua, const char *cmd); /* handler */
116    const char *help;            /* main purpose */
117    const char *usage;           /* all arguments to build usage */
118    const bool use_in_rs;        /* Can use it in Console RunScript */
119 };
120 static struct cmdstruct commands[] = {                                      /* Can use it in Console RunScript*/
121  { NT_("add"),        add_cmd,     _("Add media to a pool"),   NT_("pool=<pool-name> storage=<storage> jobid=<JobId>"),  false},
122  { NT_("autodisplay"), autodisplay_cmd,_("Autodisplay console messages"), NT_("on | off"),    false},
123  { NT_("automount"),   automount_cmd,  _("Automount after label"),        NT_("on | off"),    false},
124  { NT_("cancel"),     cancel_cmd,    _("Cancel a job"), NT_("jobid=<number> job=<job-name> ujobid=<unique-jobid>"), false},
125  { NT_("create"),     create_cmd,    _("Create DB Pool from resource"), NT_("pool=<pool-name>"),                    false},
126  { NT_("delete"),     delete_cmd,    _("Delete volume, pool or job"), NT_("volume=<vol-name> pool=<pool-name> jobid=<id>"), true},
127  { NT_("disable"),    disable_cmd,   _("Disable a job"), NT_("job=<name>"),        true},
128  { NT_("enable"),     enable_cmd,    _("Enable a job"), NT_("job=<name>"),          true},
129  { NT_("estimate"),   estimate_cmd,  _("Performs FileSet estimate, listing gives full listing"), 
130    NT_("fileset=<fs> client=<cli> level=<level> accurate=<yes/no> job=<job> listing"), true},
131
132  { NT_("exit"),       quit_cmd,      _("Terminate Bconsole session"), NT_(""),         false},
133  { NT_("gui"),        gui_cmd,       _("Non-interactive gui mode"),   NT_("on | off"), false},
134  { NT_("help"),       help_cmd,      _("Print help on specific command"),  
135    NT_("add autodisplay automount cancel create delete disable\n\tenable estimate exit gui label list llist"
136        "\n\tmessages memory mount prune purge python quit query\n\trestore relabel release reload run status"
137        "\n\tsetdebug setip show sqlquery time trace unmount umount\n\tupdate use var version wait"),         false},
138
139  { NT_("label"),      label_cmd,     _("Label a tape"), NT_("storage=<storage> volume=<vol> pool=<pool>"), false},
140  { NT_("list"),       list_cmd,      _("List objects from catalog"), 
141    NT_("pools | jobs | jobtotals | volume | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn>"), true},
142
143  { NT_("llist"),      llist_cmd,     _("Full or long list like list command"),
144    NT_("pools | jobs | jobtotals | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn>"), true},
145
146  { NT_("messages"),   messagescmd,   _("Display pending messages"),   NT_(""),    false},
147  { NT_("memory"),     memory_cmd,    _("Print current memory usage"), NT_(""),    true},
148  { NT_("mount"),      mount_cmd,     _("Mount storage"), 
149    NT_("storage=<storage-name> slot=<num> drive=<num> [ jobid=<id> | job=<job-name> ]"), false},
150
151  { NT_("prune"),      prunecmd,      _("Prune expired records from catalog"), 
152    NT_("files | jobs | pool=<pool> | client=<client-name> | volume=<volume-name> "), true},
153
154  { NT_("purge"),      purgecmd,      _("Purge records from catalog"), NT_("files jobs volume=<vol> [action=<action> devicetype=<type> pool=<pool> allpools storage=<st> drive=<num>]"),  true},
155  { NT_("python"),     python_cmd,    _("Python control commands"),    NT_(""),              false},
156  { NT_("quit"),       quit_cmd,      _("Terminate Bconsole session"), NT_(""),              false},
157  { NT_("query"),      querycmd,      _("Query catalog"),              NT_(""),              false},
158  { NT_("restore"),    restore_cmd,   _("Restore files"), 
159    NT_("where=</path> client=<client> storage=<storage> bootstrap=<file> "
160        "restore_job=<job>"
161        "\n\tcomment=<text> jobid=<jobid> done select all"), false},
162
163  { NT_("relabel"),    relabel_cmd,   _("Relabel a tape"), 
164    NT_("storage=<storage-name> oldvolume=<old-volume-name>\n\tvolume=<newvolume-name> pool=<pool>"), false},
165
166  { NT_("release"),    release_cmd,   _("Release storage"),  NT_("storage=<storage-name>"),      false},
167  { NT_("reload"),     reload_cmd,    _("Reload conf file"), NT_(""),                  true},
168  { NT_("run"),        run_cmd,       _("Run a job"), 
169    NT_("job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
170        "where=<directory-prefix>\n\twhen=<universal-time-specification>\n\tcomment=<text> yes"), false}, 
171
172  { NT_("status"),     status_cmd,    _("Report status"), 
173    NT_("all | dir=<dir-name> | director | client=<client-name> | storage=<storage-name> slots | days=nnn"), true},
174
175  { NT_("setdebug"),   setdebug_cmd,  _("Sets debug level"), 
176    NT_("level=<nn> trace=0/1 client=<client-name> | dir | storage=<storage-name> | all"), true},
177
178  { NT_("setip"),      setip_cmd,     _("Sets new client address -- if authorized"), NT_(""),   false},
179  { NT_("show"),       show_cmd,      _("Show resource records"), 
180    NT_("job=<xxx> |  pool=<yyy> | fileset=<aaa> schedule=<sss> | client=<zzz> | disabled | all"), true},
181
182  { NT_("sqlquery"),   sqlquerycmd,   _("Use SQL to query catalog"), NT_(""),          false},
183  { NT_("time"),       time_cmd,      _("Print current time"),       NT_(""),          true},
184  { NT_("trace"),      trace_cmd,     _("Turn on/off trace to file"), NT_("on | off"), true},
185  { NT_("unmount"),    unmount_cmd,   _("Unmount storage"), 
186    NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
187
188  { NT_("umount"),     unmount_cmd,   _("Umount - for old-time Unix guys, see unmount"), 
189    NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
190
191  { NT_("update"),     update_cmd,    _("Update volume, pool or stats"), 
192    NT_("stats\n\tpool=<poolname>\n\tslots storage=<storage> scan"
193        "\n\tvolume=<volname> volstatus=<status> volretention=<time-def>"
194        "\n\t pool=<pool> recycle=<yes/no> slot=<number>\n\t inchanger=<yes/no>"
195        "\n\t maxvolbytes=<size> maxvolfiles=<nb> maxvoljobs=<nb>"
196        "\n\t enable=<yes/no> recyclepool=<pool> actiononpurge=<action>"),true},
197  { NT_("use"),        use_cmd,       _("Use catalog xxx"), NT_(""),     false},
198  { NT_("var"),        var_cmd,       _("Does variable expansion"), NT_(""),  false},
199  { NT_("version"),    version_cmd,   _("Print Director version"),  NT_(""),  true},
200  { NT_("wait"),       wait_cmd,      _("Wait until no jobs are running"), 
201    NT_("jobname=<name> | jobid=<nnn> | ujobid=<complete_name>"), false}
202 };
203
204 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
205
206 /*
207  * Execute a command from the UA
208  */
209 bool do_a_command(UAContext *ua)
210 {
211    int i;
212    int len;
213    bool ok = false;
214    bool found = false;
215    BSOCK *user = ua->UA_sock;
216
217
218    Dmsg1(900, "Command: %s\n", ua->argk[0]);
219    if (ua->argc == 0) {
220       return false;
221    }
222
223    while (ua->jcr->wstorage->size()) {
224       ua->jcr->wstorage->remove(0);
225    }
226
227    len = strlen(ua->argk[0]);
228    for (i=0; i<comsize; i++) {     /* search for command */
229       if (strncasecmp(ua->argk[0],  commands[i].key, len) == 0) {
230          /* Check if command permitted, but "quit" is always OK */
231          if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
232              !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
233             break;
234          }
235          /* Check if this command is authorized in RunScript */
236          if (ua->runscript && !commands[i].use_in_rs) {
237             ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
238             break;
239          }
240          if (ua->api) user->signal(BNET_CMD_BEGIN);
241          ok = (*commands[i].func)(ua, ua->cmd);   /* go execute command */
242          if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
243          found = true;
244          break;
245       }
246    }
247    if (!found) {
248       ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
249       ok = false;
250    }
251    return ok;
252 }
253
254 /*
255  * This is a common routine used to stuff the Pool DB record defaults
256  *   into the Media DB record just before creating a media (Volume)
257  *   record.
258  */
259 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
260 {
261    mr->PoolId = pr->PoolId;
262    bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
263    mr->Recycle = pr->Recycle;
264    mr->VolRetention = pr->VolRetention;
265    mr->VolUseDuration = pr->VolUseDuration;
266    mr->ActionOnPurge = pr->ActionOnPurge;
267    mr->RecyclePoolId = pr->RecyclePoolId;
268    mr->MaxVolJobs = pr->MaxVolJobs;
269    mr->MaxVolFiles = pr->MaxVolFiles;
270    mr->MaxVolBytes = pr->MaxVolBytes;
271    mr->LabelType = pr->LabelType;
272    mr->Enabled = 1;
273 }
274
275
276 /*
277  *  Add Volumes to an existing Pool
278  */
279 static int add_cmd(UAContext *ua, const char *cmd)
280 {
281    POOL_DBR pr;
282    MEDIA_DBR mr;
283    int num, i, max, startnum;
284    int first_id = 0;
285    char name[MAX_NAME_LENGTH];
286    STORE *store;
287    int Slot = 0, InChanger = 0;
288
289    ua->send_msg(_(
290 "You probably don't want to be using this command since it\n"
291 "creates database records without labeling the Volumes.\n"
292 "You probably want to use the \"label\" command.\n\n"));
293
294    if (!open_client_db(ua)) {
295       return 1;
296    }
297
298    memset(&pr, 0, sizeof(pr));
299    memset(&mr, 0, sizeof(mr));
300
301    if (!get_pool_dbr(ua, &pr)) {
302       return 1;
303    }
304
305    Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
306       pr.MaxVols, pr.PoolType);
307
308    while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
309       ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
310       if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
311          return 1;
312       }
313       pr.MaxVols = ua->pint32_val;
314    }
315
316    /* Get media type */
317    if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
318       bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
319    } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
320       return 1;
321    }
322
323    if (pr.MaxVols == 0) {
324       max = 1000;
325    } else {
326       max = pr.MaxVols - pr.NumVols;
327    }
328    for (;;) {
329       char buf[100];
330       bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
331       if (!get_pint(ua, buf)) {
332          return 1;
333       }
334       num = ua->pint32_val;
335       if (num < 0 || num > max) {
336          ua->warning_msg(_("The number must be between 0 and %d\n"), max);
337          continue;
338       }
339       break;
340    }
341
342    for (;;) {
343       if (num == 0) {
344          if (!get_cmd(ua, _("Enter Volume name: "))) {
345             return 1;
346          }
347       } else {
348          if (!get_cmd(ua, _("Enter base volume name: "))) {
349             return 1;
350          }
351       }
352       /* Don't allow | in Volume name because it is the volume separator character */
353       if (!is_volume_name_legal(ua, ua->cmd)) {
354          continue;
355       }
356       if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
357          ua->warning_msg(_("Volume name too long.\n"));
358          continue;
359       }
360       if (strlen(ua->cmd) == 0) {
361          ua->warning_msg(_("Volume name must be at least one character long.\n"));
362          continue;
363       }
364       break;
365    }
366
367    bstrncpy(name, ua->cmd, sizeof(name));
368    if (num > 0) {
369       bstrncat(name, "%04d", sizeof(name));
370
371       for (;;) {
372          if (!get_pint(ua, _("Enter the starting number: "))) {
373             return 1;
374          }
375          startnum = ua->pint32_val;
376          if (startnum < 1) {
377             ua->warning_msg(_("Start number must be greater than zero.\n"));
378             continue;
379          }
380          break;
381       }
382    } else {
383       startnum = 1;
384       num = 1;
385    }
386
387    if (store && store->autochanger) {
388       if (!get_pint(ua, _("Enter slot (0 for none): "))) {
389          return 1;
390       }
391       Slot = ua->pint32_val;
392       if (!get_yesno(ua, _("InChanger? yes/no: "))) {
393          return 1;
394       }
395       InChanger = ua->pint32_val;
396    }
397
398    set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
399    for (i=startnum; i < num+startnum; i++) {
400       bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
401       mr.Slot = Slot++;
402       mr.InChanger = InChanger;
403       mr.StorageId = store->StorageId;
404       mr.Enabled = 1;
405       Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
406       if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
407          ua->error_msg("%s", db_strerror(ua->db));
408          return 1;
409       }
410       if (i == startnum) {
411          first_id = mr.PoolId;
412       }
413    }
414    pr.NumVols += num;
415    Dmsg0(200, "Update pool record.\n");
416    if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
417       ua->warning_msg("%s", db_strerror(ua->db));
418       return 1;
419    }
420    ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
421
422    return 1;
423 }
424
425 /*
426  * Turn auto mount on/off
427  *
428  *  automount on
429  *  automount off
430  */
431 int automount_cmd(UAContext *ua, const char *cmd)
432 {
433    char *onoff;
434
435    if (ua->argc != 2) {
436       if (!get_cmd(ua, _("Turn on or off? "))) {
437             return 1;
438       }
439       onoff = ua->cmd;
440    } else {
441       onoff = ua->argk[1];
442    }
443
444    ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
445    return 1;
446 }
447
448
449 /*
450  * Cancel a job
451  */
452 static int cancel_cmd(UAContext *ua, const char *cmd)
453 {
454    int i, ret;
455    int njobs = 0;
456    JCR *jcr = NULL;
457    char JobName[MAX_NAME_LENGTH];
458
459    for (i=1; i<ua->argc; i++) {
460       if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
461          uint32_t JobId;
462          JobId = str_to_int64(ua->argv[i]);
463          if (!JobId) {
464             break;
465          }
466          if (!(jcr=get_jcr_by_id(JobId))) {
467             ua->error_msg(_("JobId %s is not running. Use Job name to cancel inactive jobs.\n"),  ua->argv[i]);
468             return 1;
469          }
470          break;
471       } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
472          if (!ua->argv[i]) {
473             break;
474          }
475          if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
476             ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
477             jcr = new_jcr(sizeof(JCR), dird_free_jcr);
478             bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
479          }
480          break;
481       } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
482          if (!ua->argv[i]) {
483             break;
484          }
485          if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
486             ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
487             jcr = new_jcr(sizeof(JCR), dird_free_jcr);
488             bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
489          }
490          break;
491       }
492
493    }
494    if (jcr) {
495       if (jcr->job && !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
496          ua->error_msg(_("Unauthorized command from this console.\n"));
497          return 1;
498       }
499    } else {
500      /*
501       * If we still do not have a jcr,
502       *   throw up a list and ask the user to select one.
503       */
504       char buf[1000];
505       int tjobs = 0;                  /* total # number jobs */
506       /* Count Jobs running */
507       foreach_jcr(jcr) {
508          if (jcr->JobId == 0) {      /* this is us */
509             continue;
510          }
511          tjobs++;                    /* count of all jobs */
512          if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
513             continue;               /* skip not authorized */
514          }
515          njobs++;                   /* count of authorized jobs */
516       }
517       endeach_jcr(jcr);
518
519       if (njobs == 0) {            /* no authorized */
520          if (tjobs == 0) {
521             ua->send_msg(_("No Jobs running.\n"));
522          } else {
523             ua->send_msg(_("None of your jobs are running.\n"));
524          }
525          return 1;
526       }
527
528       start_prompt(ua, _("Select Job:\n"));
529       foreach_jcr(jcr) {
530          char ed1[50];
531          if (jcr->JobId == 0) {      /* this is us */
532             continue;
533          }
534          if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
535             continue;               /* skip not authorized */
536          }
537          bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
538          add_prompt(ua, buf);
539       }
540       endeach_jcr(jcr);
541
542       if (do_prompt(ua, _("Job"),  _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
543          return 1;
544       }
545       if (ua->api && njobs == 1) {
546          char nbuf[1000];
547          bsnprintf(nbuf, sizeof(nbuf), _("Cancel: %s\n\n%s"), buf,  
548                    _("Confirm cancel?"));
549          if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
550             return 1;
551          }
552       } else {
553          if (njobs == 1) {
554             if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
555                return 1;
556             }
557          }
558       }
559       sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
560       jcr = get_jcr_by_full_name(JobName);
561       if (!jcr) {
562          ua->warning_msg(_("Job \"%s\" not found.\n"), JobName);
563          return 1;
564       }
565    }
566
567    ret = cancel_job(ua, jcr);
568    free_jcr(jcr);
569    return ret;
570 }
571
572 /*
573  * This is a common routine to create or update a
574  *   Pool DB base record from a Pool Resource. We handle
575  *   the setting of MaxVols and NumVols slightly differently
576  *   depending on if we are creating the Pool or we are
577  *   simply bringing it into agreement with the resource (updage).
578  *
579  * Caution : RecyclePoolId isn't setup in this function.
580  *           You can use set_pooldbr_recyclepoolid();
581  *
582  */
583 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
584 {
585    bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
586    if (op == POOL_OP_CREATE) {
587       pr->MaxVols = pool->max_volumes;
588       pr->NumVols = 0;
589    } else {          /* update pool */
590       if (pr->MaxVols != pool->max_volumes) {
591          pr->MaxVols = pool->max_volumes;
592       }
593       if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
594          pr->MaxVols = pr->NumVols;
595       }
596    }
597    pr->LabelType = pool->LabelType;
598    pr->UseOnce = pool->use_volume_once;
599    pr->UseCatalog = pool->use_catalog;
600    pr->Recycle = pool->Recycle;
601    pr->VolRetention = pool->VolRetention;
602    pr->VolUseDuration = pool->VolUseDuration;
603    pr->MaxVolJobs = pool->MaxVolJobs;
604    pr->MaxVolFiles = pool->MaxVolFiles;
605    pr->MaxVolBytes = pool->MaxVolBytes;
606    pr->AutoPrune = pool->AutoPrune;
607    pr->ActionOnPurge = pool->action_on_purge;
608    pr->Recycle = pool->Recycle;
609    if (pool->label_format) {
610       bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
611    } else {
612       bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat));    /* none */
613    }
614 }
615
616 /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
617 int update_pool_references(JCR *jcr, B_DB *db, POOL *pool)
618 {
619    POOL_DBR  pr;
620
621    if (!pool->RecyclePool && !pool->ScratchPool) {
622       return 1;
623    }
624
625    memset(&pr, 0, sizeof(POOL_DBR));
626    bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
627
628    if (!db_get_pool_record(jcr, db, &pr)) {
629       return -1;                       /* not exists in database */
630    }
631
632    set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
633
634    if (!set_pooldbr_references(jcr, db, &pr, pool)) {
635       return -1;                      /* error */
636    }
637
638    if (!db_update_pool_record(jcr, db, &pr)) {
639       return -1;                      /* error */
640    }
641    return 1;
642 }
643
644 /* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource 
645  * works with set_pooldbr_from_poolres
646  */
647 bool set_pooldbr_references(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
648 {
649    POOL_DBR rpool;
650    bool ret = true;
651
652    if (pool->RecyclePool) {
653       memset(&rpool, 0, sizeof(POOL_DBR));
654
655       bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
656       if (db_get_pool_record(jcr, db, &rpool)) {
657         pr->RecyclePoolId = rpool.PoolId;
658       } else {
659         Jmsg(jcr, M_WARNING, 0,
660         _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
661           "Try to update it with 'update pool=%s'\n"),
662         pool->name(), rpool.Name, rpool.Name,pool->name());
663
664         ret = false;
665       }
666    } else {                    /* no RecyclePool used, set it to 0 */
667       pr->RecyclePoolId = 0;
668    }
669
670    if (pool->ScratchPool) {
671       memset(&rpool, 0, sizeof(POOL_DBR));
672
673       bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
674       if (db_get_pool_record(jcr, db, &rpool)) {
675         pr->ScratchPoolId = rpool.PoolId;
676       } else {
677         Jmsg(jcr, M_WARNING, 0,
678         _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
679           "Try to update it with 'update pool=%s'\n"),
680         pool->name(), rpool.Name, rpool.Name,pool->name());
681         ret = false;
682       }
683    } else {                    /* no ScratchPool used, set it to 0 */
684       pr->ScratchPoolId = 0;
685    }
686  
687    return ret;
688 }
689
690
691 /*
692  * Create a pool record from a given Pool resource
693  *   Also called from backup.c
694  * Returns: -1  on error
695  *           0  record already exists
696  *           1  record created
697  */
698
699 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
700 {
701    POOL_DBR  pr;
702
703    memset(&pr, 0, sizeof(POOL_DBR));
704
705    bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
706
707    if (db_get_pool_record(jcr, db, &pr)) {
708       /* Pool Exists */
709       if (op == POOL_OP_UPDATE) {  /* update request */
710          set_pooldbr_from_poolres(&pr, pool, op);
711          set_pooldbr_references(jcr, db, &pr, pool);
712          db_update_pool_record(jcr, db, &pr);
713       }
714       return 0;                       /* exists */
715    }
716
717    set_pooldbr_from_poolres(&pr, pool, op);
718    set_pooldbr_references(jcr, db, &pr, pool);
719
720    if (!db_create_pool_record(jcr, db, &pr)) {
721       return -1;                      /* error */
722    }
723    return 1;
724 }
725
726
727
728 /*
729  * Create a Pool Record in the database.
730  *  It is always created from the Resource record.
731  */
732 static int create_cmd(UAContext *ua, const char *cmd)
733 {
734    POOL *pool;
735
736    if (!open_client_db(ua)) {
737       return 1;
738    }
739
740    pool = get_pool_resource(ua);
741    if (!pool) {
742       return 1;
743    }
744
745    switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
746    case 0:
747       ua->error_msg(_("Error: Pool %s already exists.\n"
748                "Use update to change it.\n"), pool->name());
749       break;
750
751    case -1:
752       ua->error_msg("%s", db_strerror(ua->db));
753       break;
754
755    default:
756      break;
757    }
758    ua->send_msg(_("Pool %s created.\n"), pool->name());
759    return 1;
760 }
761
762
763 extern DIRRES *director;
764 extern char *configfile;
765
766 /*
767  * Python control command
768  *  python restart (restarts interpreter)
769  */
770 static int python_cmd(UAContext *ua, const char *cmd)
771 {
772 #ifdef HAVE_PYTHON
773    init_python_interpreter_args python_args;
774
775    if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
776       term_python_interpreter();
777
778       python_args.progname = director->name();
779       python_args.scriptdir = director->scripts_directory;
780       python_args.modulename = "DirStartUp";
781       python_args.configfile = configfile;
782       python_args.workingdir = director->working_directory;
783       python_args.job_getattr = job_getattr;
784       python_args.job_setattr = job_setattr;
785
786       init_python_interpreter(&python_args);
787
788       ua->send_msg(_("Python interpreter restarted.\n"));
789    } else {
790 #endif /* HAVE_PYTHON */
791       ua->warning_msg(_("Nothing done.\n"));
792 #ifdef HAVE_PYTHON
793    }
794 #endif /* HAVE_PYTHON */
795    return 1;
796 }
797
798
799 /*
800  * Set a new address in a Client resource. We do this only
801  *  if the Console name is the same as the Client name
802  *  and the Console can access the client.
803  */
804 static int setip_cmd(UAContext *ua, const char *cmd)
805 {
806    CLIENT *client;
807    char buf[1024];
808    if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
809       ua->error_msg(_("Unauthorized command from this console.\n"));
810       return 1;
811    }
812    LockRes();
813    client = GetClientResWithName(ua->cons->name());
814
815    if (!client) {
816       ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
817       goto get_out;
818    }
819    if (client->address) {
820       free(client->address);
821    }
822    /* MA Bug 6 remove ifdef */
823    sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
824    client->address = bstrdup(buf);
825    ua->send_msg(_("Client \"%s\" address set to %s\n"),
826             client->name(), client->address);
827 get_out:
828    UnlockRes();
829    return 1;
830 }
831
832
833 static void do_en_disable_cmd(UAContext *ua, bool setting)
834 {
835    JOB *job;
836    int i;
837
838    i = find_arg_with_value(ua, NT_("job")); 
839    if (i < 0) { 
840       job = select_enable_disable_job_resource(ua, setting);
841       if (!job) {
842          return;
843       }
844    } else {
845       LockRes();
846       job = GetJobResWithName(ua->argv[i]);
847       UnlockRes();
848    } 
849    if (!job) {
850       ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
851       return;
852    }
853
854    if (!acl_access_ok(ua, Job_ACL, job->name())) {
855       ua->error_msg(_("Unauthorized command from this console.\n"));
856       return;
857    }
858    job->enabled = setting;
859    ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
860    return;
861 }
862
863 static int enable_cmd(UAContext *ua, const char *cmd)
864 {
865    do_en_disable_cmd(ua, true);
866    return 1;
867 }
868
869 static int disable_cmd(UAContext *ua, const char *cmd)
870 {
871    do_en_disable_cmd(ua, false);
872    return 1;
873 }
874
875
876 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
877 {
878    BSOCK *sd;
879    JCR *jcr = ua->jcr;
880    USTORE lstore;
881    
882    lstore.store = store;
883    pm_strcpy(lstore.store_source, _("unknown source"));
884    set_wstorage(jcr, &lstore);
885    /* Try connecting for up to 15 seconds */
886    ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
887       store->name(), store->address, store->SDport);
888    if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
889       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
890       return;
891    }
892    Dmsg0(120, _("Connected to storage daemon\n"));
893    sd = jcr->store_bsock;
894    sd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
895    if (sd->recv() >= 0) {
896       ua->send_msg("%s", sd->msg);
897    }
898    sd->signal(BNET_TERMINATE);
899    sd->close();
900    jcr->store_bsock = NULL;
901    return;
902 }
903
904 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
905 {
906    BSOCK *fd;
907
908    /* Connect to File daemon */
909
910    ua->jcr->client = client;
911    /* Try to connect for 15 seconds */
912    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
913       client->name(), client->address, client->FDport);
914    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
915       ua->error_msg(_("Failed to connect to Client.\n"));
916       return;
917    }
918    Dmsg0(120, "Connected to file daemon\n");
919    fd = ua->jcr->file_bsock;
920    fd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
921    if (fd->recv() >= 0) {
922       ua->send_msg("%s", fd->msg);
923    }
924    fd->signal(BNET_TERMINATE);
925    fd->close();
926    ua->jcr->file_bsock = NULL;
927    return;
928 }
929
930
931 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
932 {
933    STORE *store, **unique_store;
934    CLIENT *client, **unique_client;
935    int i, j, found;
936
937    /* Director */
938    debug_level = level;
939
940    /* Count Storage items */
941    LockRes();
942    store = NULL;
943    i = 0;
944    foreach_res(store, R_STORAGE) {
945       i++;
946    }
947    unique_store = (STORE **) malloc(i * sizeof(STORE));
948    /* Find Unique Storage address/port */
949    store = (STORE *)GetNextRes(R_STORAGE, NULL);
950    i = 0;
951    unique_store[i++] = store;
952    while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
953       found = 0;
954       for (j=0; j<i; j++) {
955          if (strcmp(unique_store[j]->address, store->address) == 0 &&
956              unique_store[j]->SDport == store->SDport) {
957             found = 1;
958             break;
959          }
960       }
961       if (!found) {
962          unique_store[i++] = store;
963          Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
964       }
965    }
966    UnlockRes();
967
968    /* Call each unique Storage daemon */
969    for (j=0; j<i; j++) {
970       do_storage_setdebug(ua, unique_store[j], level, trace_flag);
971    }
972    free(unique_store);
973
974    /* Count Client items */
975    LockRes();
976    client = NULL;
977    i = 0;
978    foreach_res(client, R_CLIENT) {
979       i++;
980    }
981    unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
982    /* Find Unique Client address/port */
983    client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
984    i = 0;
985    unique_client[i++] = client;
986    while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
987       found = 0;
988       for (j=0; j<i; j++) {
989          if (strcmp(unique_client[j]->address, client->address) == 0 &&
990              unique_client[j]->FDport == client->FDport) {
991             found = 1;
992             break;
993          }
994       }
995       if (!found) {
996          unique_client[i++] = client;
997          Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
998       }
999    }
1000    UnlockRes();
1001
1002    /* Call each unique File daemon */
1003    for (j=0; j<i; j++) {
1004       do_client_setdebug(ua, unique_client[j], level, trace_flag);
1005    }
1006    free(unique_client);
1007 }
1008
1009 /*
1010  * setdebug level=nn all trace=1/0
1011  */
1012 static int setdebug_cmd(UAContext *ua, const char *cmd)
1013 {
1014    STORE *store;
1015    CLIENT *client;
1016    int level;
1017    int trace_flag = -1;
1018    int i;
1019
1020    Dmsg1(120, "setdebug:%s:\n", cmd);
1021
1022    level = -1;
1023    i = find_arg_with_value(ua, "level");
1024    if (i >= 0) {
1025       level = atoi(ua->argv[i]);
1026    }
1027    if (level < 0) {
1028       if (!get_pint(ua, _("Enter new debug level: "))) {
1029          return 1;
1030       }
1031       level = ua->pint32_val;
1032    }
1033
1034    /* Look for trace flag. -1 => not change */
1035    i = find_arg_with_value(ua, "trace");
1036    if (i >= 0) {
1037       trace_flag = atoi(ua->argv[i]);
1038       if (trace_flag > 0) {
1039          trace_flag = 1;
1040       }
1041    }
1042
1043    /* General debug? */
1044    for (i=1; i<ua->argc; i++) {
1045       if (strcasecmp(ua->argk[i], "all") == 0) {
1046          do_all_setdebug(ua, level, trace_flag);
1047          return 1;
1048       }
1049       if (strcasecmp(ua->argk[i], "dir") == 0 ||
1050           strcasecmp(ua->argk[i], "director") == 0) {
1051          debug_level = level;
1052          set_trace(trace_flag);
1053          return 1;
1054       }
1055       if (strcasecmp(ua->argk[i], "client") == 0 ||
1056           strcasecmp(ua->argk[i], "fd") == 0) {
1057          client = NULL;
1058          if (ua->argv[i]) {
1059             client = GetClientResWithName(ua->argv[i]);
1060             if (client) {
1061                do_client_setdebug(ua, client, level, trace_flag);
1062                return 1;
1063             }
1064          }
1065          client = select_client_resource(ua);
1066          if (client) {
1067             do_client_setdebug(ua, client, level, trace_flag);
1068             return 1;
1069          }
1070       }
1071
1072       if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1073           strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1074           strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1075          store = NULL;
1076          if (ua->argv[i]) {
1077             store = GetStoreResWithName(ua->argv[i]);
1078             if (store) {
1079                do_storage_setdebug(ua, store, level, trace_flag);
1080                return 1;
1081             }
1082          }
1083          store = get_storage_resource(ua, false/*no default*/);
1084          if (store) {
1085             do_storage_setdebug(ua, store, level, trace_flag);
1086             return 1;
1087          }
1088       }
1089    }
1090    /*
1091     * We didn't find an appropriate keyword above, so
1092     * prompt the user.
1093     */
1094    start_prompt(ua, _("Available daemons are: \n"));
1095    add_prompt(ua, _("Director"));
1096    add_prompt(ua, _("Storage"));
1097    add_prompt(ua, _("Client"));
1098    add_prompt(ua, _("All"));
1099    switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1100    case 0:                         /* Director */
1101       debug_level = level;
1102       set_trace(trace_flag);
1103       break;
1104    case 1:
1105       store = get_storage_resource(ua, false/*no default*/);
1106       if (store) {
1107          do_storage_setdebug(ua, store, level, trace_flag);
1108       }
1109       break;
1110    case 2:
1111       client = select_client_resource(ua);
1112       if (client) {
1113          do_client_setdebug(ua, client, level, trace_flag);
1114       }
1115       break;
1116    case 3:
1117       do_all_setdebug(ua, level, trace_flag);
1118       break;
1119    default:
1120       break;
1121    }
1122    return 1;
1123 }
1124
1125 /*
1126  * Turn debug tracing to file on/off
1127  */
1128 static int trace_cmd(UAContext *ua, const char *cmd)
1129 {
1130    char *onoff;
1131
1132    if (ua->argc != 2) {
1133       if (!get_cmd(ua, _("Turn on or off? "))) {
1134             return 1;
1135       }
1136       onoff = ua->cmd;
1137    } else {
1138       onoff = ua->argk[1];
1139    }
1140
1141    set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1142    return 1;
1143
1144 }
1145
1146 static int var_cmd(UAContext *ua, const char *cmd)
1147 {
1148    POOLMEM *val = get_pool_memory(PM_FNAME);
1149    char *var;
1150
1151    if (!open_client_db(ua)) {
1152       return 1;
1153    }
1154    for (var=ua->cmd; *var != ' '; ) {    /* skip command */
1155       var++;
1156    }
1157    while (*var == ' ') {                 /* skip spaces */
1158       var++;
1159    }
1160    Dmsg1(100, "Var=%s:\n", var);
1161    variable_expansion(ua->jcr, var, &val);
1162    ua->send_msg("%s\n", val);
1163    free_pool_memory(val);
1164    return 1;
1165 }
1166
1167 static int estimate_cmd(UAContext *ua, const char *cmd)
1168 {
1169    JOB *job = NULL;
1170    CLIENT *client = NULL;
1171    FILESET *fileset = NULL;
1172    int listing = 0;
1173    char since[MAXSTRING];
1174    JCR *jcr = ua->jcr;
1175    int accurate=-1;
1176
1177    jcr->set_JobLevel(L_FULL);
1178    for (int i=1; i<ua->argc; i++) {
1179       if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1180           strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1181          if (ua->argv[i]) {
1182             client = GetClientResWithName(ua->argv[i]);
1183             if (!client) {
1184                ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1185                return 1;
1186             }
1187             if (!acl_access_ok(ua, Client_ACL, client->name())) {
1188                ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1189                return 1;
1190             }
1191             continue;
1192          } else {
1193             ua->error_msg(_("Client name missing.\n"));
1194             return 1;
1195          }
1196       }
1197       if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1198          if (ua->argv[i]) {
1199             job = GetJobResWithName(ua->argv[i]);
1200             if (!job) {
1201                ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1202                return 1;
1203             }
1204             if (!acl_access_ok(ua, Job_ACL, job->name())) {
1205                ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1206                return 1;
1207             }
1208             continue;
1209          } else {
1210             ua->error_msg(_("Job name missing.\n"));
1211             return 1;
1212          }
1213
1214       }
1215       if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1216          if (ua->argv[i]) {
1217             fileset = GetFileSetResWithName(ua->argv[i]);
1218             if (!fileset) {
1219                ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1220                return 1;
1221             }
1222             if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1223                ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1224                return 1;
1225             }
1226             continue;
1227          } else {
1228             ua->error_msg(_("Fileset name missing.\n"));
1229             return 1;
1230          }
1231       }
1232       if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1233          listing = 1;
1234          continue;
1235       }
1236       if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1237          if (ua->argv[i]) {
1238             if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1239                ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1240             }
1241             continue;
1242          } else {
1243            ua->error_msg(_("Level value missing.\n"));
1244            return 1;
1245          }
1246       }
1247       if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1248          if (!is_yesno(ua->argv[i], &accurate)) {
1249             ua->error_msg(_("Invalid value for accurate. " 
1250                             "It must be yes or no.\n"));
1251          }
1252       }
1253    }
1254    if (!job && !(client && fileset)) {
1255       if (!(job = select_job_resource(ua))) {
1256          return 1;
1257       }
1258    }
1259    if (!job) {
1260       job = GetJobResWithName(ua->argk[1]);
1261       if (!job) {
1262          ua->error_msg(_("No job specified.\n"));
1263          return 1;
1264       }
1265       if (!acl_access_ok(ua, Job_ACL, job->name())) {
1266          ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1267          return 1;
1268       }
1269    }
1270    if (!client) {
1271       client = job->client;
1272    }
1273    if (!fileset) {
1274       fileset = job->fileset;
1275    }
1276    jcr->client = client;
1277    jcr->fileset = fileset;
1278    close_db(ua);
1279    if (job->pool->catalog) {
1280       ua->catalog = job->pool->catalog;
1281    } else {
1282       ua->catalog = client->catalog;
1283    }
1284
1285    if (!open_db(ua)) {
1286       return 1;
1287    }
1288
1289    jcr->job = job;
1290    jcr->set_JobType(JT_BACKUP);
1291    init_jcr_job_record(jcr);
1292
1293    if (!get_or_create_client_record(jcr)) {
1294       return 1;
1295    }
1296    if (!get_or_create_fileset_record(jcr)) {
1297       return 1;
1298    }
1299
1300    get_level_since_time(ua->jcr, since, sizeof(since));
1301
1302    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1303       jcr->client->name(), jcr->client->address, jcr->client->FDport);
1304    if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1305       ua->error_msg(_("Failed to connect to Client.\n"));
1306       return 1;
1307    }
1308
1309    if (!send_include_list(jcr)) {
1310       ua->error_msg(_("Error sending include list.\n"));
1311       goto bail_out;
1312    }
1313
1314    if (!send_exclude_list(jcr)) {
1315       ua->error_msg(_("Error sending exclude list.\n"));
1316       goto bail_out;
1317    }
1318
1319    /* The level string change if accurate mode is enabled */
1320    if (accurate >= 0) {
1321       jcr->accurate = accurate;
1322    } else {
1323       jcr->accurate = job->accurate;
1324    }
1325
1326    if (!send_level_command(jcr)) {
1327       goto bail_out;
1328    }
1329
1330    /*
1331     * If the job is in accurate mode, we send the list of
1332     * all files to FD.
1333     */
1334    Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1335    if (!send_accurate_current_files(jcr)) {
1336       goto bail_out;
1337    }
1338
1339    bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1340    while (bnet_recv(jcr->file_bsock) >= 0) {
1341       ua->send_msg("%s", jcr->file_bsock->msg);
1342    }
1343
1344 bail_out:
1345    if (jcr->file_bsock) {
1346       jcr->file_bsock->signal(BNET_TERMINATE);
1347       jcr->file_bsock->close();
1348       jcr->file_bsock = NULL;
1349    }
1350    return 1;
1351 }
1352
1353
1354 /*
1355  * print time
1356  */
1357 static int time_cmd(UAContext *ua, const char *cmd)
1358 {
1359    char sdt[50];
1360    time_t ttime = time(NULL);
1361    struct tm tm;
1362    (void)localtime_r(&ttime, &tm);
1363    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1364    ua->send_msg("%s\n", sdt);
1365    return 1;
1366 }
1367
1368 /*
1369  * reload the conf file
1370  */
1371 extern "C" void reload_config(int sig);
1372
1373 static int reload_cmd(UAContext *ua, const char *cmd)
1374 {
1375    reload_config(1);
1376    return 1;
1377 }
1378
1379 /*
1380  * Delete Pool records (should purge Media with it).
1381  *
1382  *  delete pool=<pool-name>
1383  *  delete volume pool=<pool-name> volume=<name>
1384  *  delete jobid=xxx
1385  */
1386 static int delete_cmd(UAContext *ua, const char *cmd)
1387 {
1388    static const char *keywords[] = {
1389       NT_("volume"),
1390       NT_("pool"),
1391       NT_("jobid"),
1392       NULL};
1393
1394    if (!open_client_db(ua)) {
1395       return 1;
1396    }
1397
1398    switch (find_arg_keyword(ua, keywords)) {
1399    case 0:
1400       delete_volume(ua);
1401       return 1;
1402    case 1:
1403       delete_pool(ua);
1404       return 1;
1405    case 2:
1406       int i;
1407       while ((i=find_arg(ua, "jobid")) > 0) {
1408          delete_job(ua);
1409          *ua->argk[i] = 0;         /* zap keyword already visited */
1410       }
1411       return 1;
1412    default:
1413       break;
1414    }
1415
1416    ua->warning_msg(_(
1417 "In general it is not a good idea to delete either a\n"
1418 "Pool or a Volume since they may contain data.\n\n"));
1419
1420    switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1421    case 0:
1422       delete_volume(ua);
1423       break;
1424    case 1:
1425       delete_pool(ua);
1426       break;
1427    case 2:
1428       delete_job(ua);
1429       return 1;
1430    default:
1431       ua->warning_msg(_("Nothing done.\n"));
1432       break;
1433    }
1434    return 1;
1435 }
1436
1437
1438 /*
1439  * delete_job has been modified to parse JobID lists like the
1440  * following:
1441  * delete JobID=3,4,6,7-11,14
1442  *
1443  * Thanks to Phil Stracchino for the above addition.
1444  */
1445
1446 static void delete_job(UAContext *ua)
1447 {
1448    JobId_t JobId;
1449    char *s,*sep,*tok;
1450
1451    int i = find_arg_with_value(ua, NT_("jobid"));
1452    if (i >= 0) {
1453       if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1454         s = bstrdup(ua->argv[i]);
1455         tok = s;
1456         /*
1457          * We could use strtok() here.  But we're not going to, because:
1458          * (a) strtok() is deprecated, having been replaced by strsep();
1459          * (b) strtok() is broken in significant ways.
1460          * we could use strsep() instead, but it's not universally available.
1461          * so we grow our own using strchr().
1462          */
1463         sep = strchr(tok, ',');
1464         while (sep != NULL) {
1465            *sep = '\0';
1466            if (!delete_job_id_range(ua, tok)) {
1467               JobId = str_to_int64(tok);
1468               do_job_delete(ua, JobId);
1469            }
1470            tok = ++sep;
1471            sep = strchr(tok, ',');
1472         }
1473         /* pick up the last token */
1474         if (!delete_job_id_range(ua, tok)) {
1475             JobId = str_to_int64(tok);
1476             do_job_delete(ua, JobId);
1477         }
1478
1479          free(s);
1480       } else {
1481          JobId = str_to_int64(ua->argv[i]);
1482         do_job_delete(ua, JobId);
1483       }
1484    } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1485       return;
1486    } else {
1487       JobId = ua->int64_val;
1488       do_job_delete(ua, JobId);
1489    }
1490 }
1491
1492 /*
1493  * we call delete_job_id_range to parse range tokens and iterate over ranges
1494  */
1495 static bool delete_job_id_range(UAContext *ua, char *tok)
1496 {
1497    char *tok2;
1498    JobId_t j,j1,j2;
1499
1500    tok2 = strchr(tok, '-');
1501    if (!tok2) {
1502       return false;
1503    }
1504    *tok2 = '\0';
1505    tok2++;
1506    j1 = str_to_int64(tok);
1507    j2 = str_to_int64(tok2);
1508    for (j=j1; j<=j2; j++) {
1509       do_job_delete(ua, j);
1510    }
1511    return true;
1512 }
1513
1514 /*
1515  * do_job_delete now performs the actual delete operation atomically
1516  */
1517 static void do_job_delete(UAContext *ua, JobId_t JobId)
1518 {
1519    char ed1[50];
1520
1521    edit_int64(JobId, ed1);
1522    purge_jobs_from_catalog(ua, ed1);
1523    ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1524 }
1525
1526 /*
1527  * Delete media records from database -- dangerous
1528  */
1529 static int delete_volume(UAContext *ua)
1530 {
1531    MEDIA_DBR mr;
1532    char buf[1000];
1533    db_list_ctx lst;
1534
1535    if (!select_media_dbr(ua, &mr)) {
1536       return 1;
1537    }
1538    ua->warning_msg(_("\nThis command will delete volume %s\n"
1539       "and all Jobs saved on that volume from the Catalog\n"),
1540       mr.VolumeName);
1541
1542    if (find_arg(ua, "yes") >= 0) {
1543       ua->pint32_val = 1; /* Have "yes" on command line already" */
1544    } else {
1545       bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1546          mr.VolumeName);
1547       if (!get_yesno(ua, buf)) {
1548          return 1;
1549       }
1550    }
1551    if (!ua->pint32_val) {
1552       return 1;
1553    }
1554
1555    /* If not purged, do it */
1556    if (strcmp(mr.VolStatus, "Purged") != 0) {
1557       if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1558          ua->error_msg(_("Can't list jobs on this volume\n"));
1559          return 1;
1560       }
1561       if (lst.count) {
1562          purge_jobs_from_catalog(ua, lst.list);
1563       }
1564    }
1565
1566    db_delete_media_record(ua->jcr, ua->db, &mr);
1567    return 1;
1568 }
1569
1570 /*
1571  * Delete a pool record from the database -- dangerous
1572  */
1573 static int delete_pool(UAContext *ua)
1574 {
1575    POOL_DBR  pr;
1576    char buf[200];
1577
1578    memset(&pr, 0, sizeof(pr));
1579
1580    if (!get_pool_dbr(ua, &pr)) {
1581       return 1;
1582    }
1583    bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1584       pr.Name);
1585    if (!get_yesno(ua, buf)) {
1586       return 1;
1587    }
1588    if (ua->pint32_val) {
1589       db_delete_pool_record(ua->jcr, ua->db, &pr);
1590    }
1591    return 1;
1592 }
1593
1594 int memory_cmd(UAContext *ua, const char *cmd)
1595 {
1596    list_dir_status_header(ua);
1597    sm_dump(false, true);
1598    return 1;
1599 }
1600
1601 static void do_mount_cmd(UAContext *ua, const char *command)
1602 {
1603    USTORE store;
1604    BSOCK *sd;
1605    JCR *jcr = ua->jcr;
1606    char dev_name[MAX_NAME_LENGTH];
1607    int drive;
1608    int slot = -1;
1609
1610    if (!open_client_db(ua)) {
1611       return;
1612    }
1613    Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1614
1615    store.store = get_storage_resource(ua, true/*arg is storage*/);
1616    if (!store.store) {
1617       return;
1618    }
1619    pm_strcpy(store.store_source, _("unknown source"));
1620    set_wstorage(jcr, &store);
1621    drive = get_storage_drive(ua, store.store);
1622    if (strcmp(command, "mount") == 0) {
1623       slot = get_storage_slot(ua, store.store);
1624    }
1625
1626    Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1627       store.store->media_type, store.store->dev_name(), drive);
1628
1629    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1630       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1631       return;
1632    }
1633    sd = jcr->store_bsock;
1634    bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1635    bash_spaces(dev_name);
1636    if (slot > 0) {
1637       bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1638    } else {
1639       bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1640    }
1641    while (bnet_recv(sd) >= 0) {
1642       ua->send_msg("%s", sd->msg);
1643    }
1644    bnet_sig(sd, BNET_TERMINATE);
1645    bnet_close(sd);
1646    jcr->store_bsock = NULL;
1647 }
1648
1649 /*
1650  * mount [storage=<name>] [drive=nn] [slot=mm]
1651  */
1652 static int mount_cmd(UAContext *ua, const char *cmd)
1653 {
1654    do_mount_cmd(ua, "mount");          /* mount */
1655    return 1;
1656 }
1657
1658
1659 /*
1660  * unmount [storage=<name>] [drive=nn]
1661  */
1662 static int unmount_cmd(UAContext *ua, const char *cmd)
1663 {
1664    do_mount_cmd(ua, "unmount");          /* unmount */
1665    return 1;
1666 }
1667
1668
1669 /*
1670  * release [storage=<name>] [drive=nn]
1671  */
1672 static int release_cmd(UAContext *ua, const char *cmd)
1673 {
1674    do_mount_cmd(ua, "release");          /* release */
1675    return 1;
1676 }
1677
1678
1679 /*
1680  * Switch databases
1681  *   use catalog=<name>
1682  */
1683 static int use_cmd(UAContext *ua, const char *cmd)
1684 {
1685    CAT *oldcatalog, *catalog;
1686
1687
1688    close_db(ua);                      /* close any previously open db */
1689    oldcatalog = ua->catalog;
1690
1691    if (!(catalog = get_catalog_resource(ua))) {
1692       ua->catalog = oldcatalog;
1693    } else {
1694       ua->catalog = catalog;
1695    }
1696    if (open_db(ua)) {
1697       ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1698          ua->catalog->name(), ua->catalog->db_name);
1699    }
1700    return 1;
1701 }
1702
1703 int quit_cmd(UAContext *ua, const char *cmd)
1704 {
1705    ua->quit = true;
1706    return 1;
1707 }
1708
1709 /* Handler to get job status */
1710 static int status_handler(void *ctx, int num_fields, char **row)
1711 {
1712    char *val = (char *)ctx;
1713
1714    if (row[0]) {
1715       *val = row[0][0];
1716    } else {
1717       *val = '?';               /* Unknown by default */
1718    }
1719
1720    return 0;
1721 }
1722
1723 /*
1724  * Wait until no job is running
1725  */
1726 int wait_cmd(UAContext *ua, const char *cmd)
1727 {
1728    JCR *jcr;
1729    int i;
1730    time_t stop_time = 0;
1731
1732    /*
1733     * no args
1734     * Wait until no job is running
1735     */
1736    if (ua->argc == 1) {
1737       bmicrosleep(0, 200000);            /* let job actually start */
1738       for (bool running=true; running; ) {
1739          running = false;
1740          foreach_jcr(jcr) {
1741             if (jcr->JobId != 0) {
1742                running = true;
1743                break;
1744             }
1745          }
1746          endeach_jcr(jcr);
1747
1748          if (running) {
1749             bmicrosleep(1, 0);
1750          }
1751       }
1752       return 1;
1753    }
1754
1755    i = find_arg_with_value(ua, NT_("timeout")); 
1756    if (i > 0 && ua->argv[i]) {
1757       stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1758    }
1759
1760    /* we have jobid, jobname or ujobid argument */
1761
1762    uint32_t jobid = 0 ;
1763
1764    if (!open_client_db(ua)) {
1765       ua->error_msg(_("ERR: Can't open db\n")) ;
1766       return 1;
1767    }
1768
1769    for (int i=1; i<ua->argc; i++) {
1770       if (strcasecmp(ua->argk[i], "jobid") == 0) {
1771          if (!ua->argv[i]) {
1772             break;
1773          }
1774          jobid = str_to_int64(ua->argv[i]);
1775          break;
1776       } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1777                  strcasecmp(ua->argk[i], "job") == 0) {
1778          if (!ua->argv[i]) {
1779             break;
1780          }
1781          jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1782          if (jcr) {
1783             jobid = jcr->JobId ;
1784             free_jcr(jcr);
1785          }
1786          break;
1787       } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1788          if (!ua->argv[i]) {
1789             break;
1790          }
1791          jcr=get_jcr_by_full_name(ua->argv[i]) ;
1792          if (jcr) {
1793             jobid = jcr->JobId ;
1794             free_jcr(jcr);
1795          }
1796          break;
1797       /* Wait for a mount request */
1798       } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1799          for (bool waiting=false; !waiting; ) {
1800             foreach_jcr(jcr) {
1801                if (jcr->JobId != 0 && 
1802                    (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1803                   waiting = true;
1804                   break;
1805                }
1806             }
1807             endeach_jcr(jcr);
1808             if (waiting) {
1809                break;
1810             }
1811             if (stop_time && (time(NULL) >= stop_time)) {
1812                ua->warning_msg(_("Wait on mount timed out\n"));
1813                return 1;
1814             }
1815             bmicrosleep(1, 0);
1816          }
1817          return 1;
1818       }
1819    }
1820
1821    if (jobid == 0) {
1822       ua->error_msg(_("ERR: Job was not found\n"));
1823       return 1 ;
1824    }
1825
1826    /*
1827     * We wait the end of a specific job
1828     */
1829
1830    bmicrosleep(0, 200000);            /* let job actually start */
1831    for (bool running=true; running; ) {
1832       running = false;
1833
1834       jcr=get_jcr_by_id(jobid) ;
1835
1836       if (jcr) {
1837          running = true ;
1838          free_jcr(jcr);
1839       }
1840
1841       if (running) {
1842          bmicrosleep(1, 0);
1843       }
1844    }
1845
1846    /*
1847     * We have to get JobStatus
1848     */
1849
1850    int status ;
1851    char jobstatus = '?';        /* Unknown by default */
1852    char buf[256] ;
1853
1854    bsnprintf(buf, sizeof(buf),
1855              "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1856
1857
1858    db_sql_query(ua->db, buf,
1859                 status_handler, (void *)&jobstatus);
1860
1861    switch (jobstatus) {
1862    case JS_Error:
1863       status = 1 ;         /* Warning */
1864       break;
1865
1866    case JS_FatalError:
1867    case JS_ErrorTerminated:
1868    case JS_Canceled:
1869       status = 2 ;         /* Critical */
1870       break;
1871
1872    case JS_Warnings:
1873    case JS_Terminated:
1874       status = 0 ;         /* Ok */
1875       break;
1876
1877    default:
1878       status = 3 ;         /* Unknown */
1879       break;
1880    }
1881
1882    ua->send_msg("JobId=%i\n", jobid) ;
1883    ua->send_msg("JobStatus=%s (%c)\n", 
1884             job_status_to_str(jobstatus), 
1885             jobstatus) ;
1886
1887    if (ua->gui || ua->api) {
1888       ua->send_msg("ExitStatus=%i\n", status) ;
1889    }
1890
1891    return 1;
1892 }
1893
1894
1895 static int help_cmd(UAContext *ua, const char *cmd)
1896 {
1897    int i;
1898    ua->send_msg(_("  Command       Description\n  =======       ===========\n"));
1899    for (i=0; i<comsize; i++) {
1900       if (ua->argc == 2) {
1901          if (!strcasecmp(ua->argk[1], commands[i].key)) {
1902             ua->send_msg(_("  %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key, 
1903                          commands[i].help, commands[i].usage);
1904             break;
1905          }
1906       } else {
1907          ua->send_msg(_("  %-13s %s\n"), commands[i].key, commands[i].help);
1908       }
1909    }
1910    if (i == comsize && ua->argc == 2) {
1911       ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
1912    }
1913    ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1914    return 1;
1915 }
1916
1917 int qhelp_cmd(UAContext *ua, const char *cmd)
1918 {
1919    int i,j;
1920    /* Want to display only commands */
1921    j = find_arg(ua, NT_("all"));
1922    if (j >= 0) {
1923       for (i=0; i<comsize; i++) {
1924          ua->send_msg("%s\n", commands[i].key);
1925       }
1926       return 1;
1927    }
1928    /* Want to display a specific help section */
1929    j = find_arg_with_value(ua, NT_("item"));
1930    if (j >= 0 && ua->argk[j]) {
1931       for (i=0; i<comsize; i++) {
1932          if (bstrcmp(commands[i].key, ua->argv[j])) {
1933             ua->send_msg("%s\n", commands[i].usage);
1934             break;
1935          }
1936       }
1937       return 1;
1938    }
1939    /* Want to display everything */
1940    for (i=0; i<comsize; i++) {
1941       ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
1942    }
1943    return 1;
1944 }
1945
1946 #if 1 
1947 static int version_cmd(UAContext *ua, const char *cmd)
1948 {
1949    ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
1950                 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
1951    return 1;
1952 }
1953 #else
1954 /*
1955  *  Test code -- turned on only for debug testing 
1956  */
1957 static int version_cmd(UAContext *ua, const char *cmd)
1958 {
1959    dbid_list ids;
1960    POOL_MEM query(PM_MESSAGE);
1961    open_db(ua);
1962    Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1963    db_get_query_dbids(ua->jcr, ua->db, query, ids);
1964    ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1965    for (int i=0; i < ids.num_ids; i++) {
1966       ua->send_msg("id=%d\n", ids.DBId[i]);
1967    }
1968    close_db(ua);
1969    return 1;
1970 }
1971 #endif
1972
1973 /* 
1974  * This call uses open_client_db() and force a
1975  * new dedicated connection to the catalog
1976  */
1977 bool open_new_client_db(UAContext *ua)
1978 {   
1979    bool ret;
1980
1981    /* Force a new dedicated connection */
1982    close_db(ua);
1983    ua->force_mult_db_connections = true;
1984    ret = open_client_db(ua);
1985    ua->force_mult_db_connections = false;
1986    return ret;
1987 }
1988
1989 /* 
1990  * This call explicitly checks for a catalog=xxx and
1991  *  if given, opens that catalog.  It also checks for
1992  *  client=xxx and if found, opens the catalog 
1993  *  corresponding to that client. If we still don't 
1994  *  have a catalog, look for a Job keyword and get the
1995  *  catalog from its client record.
1996  */
1997 bool open_client_db(UAContext *ua)
1998 {
1999    int i;
2000    CAT *catalog;
2001    CLIENT *client;
2002    JOB *job;
2003
2004    /* Try for catalog keyword */
2005    i = find_arg_with_value(ua, NT_("catalog"));
2006    if (i >= 0) {
2007       if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
2008          ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
2009          return false;
2010       }
2011       catalog = GetCatalogResWithName(ua->argv[i]);
2012       if (catalog) {
2013          if (ua->catalog && ua->catalog != catalog) {
2014             close_db(ua);
2015          }
2016          ua->catalog = catalog;
2017          return open_db(ua);
2018       }
2019    }
2020
2021    /* Try for client keyword */
2022    i = find_arg_with_value(ua, NT_("client"));
2023    if (i >= 0) {
2024       if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
2025          ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2026          return false;
2027       }
2028       client = GetClientResWithName(ua->argv[i]);
2029       if (client) {
2030          catalog = client->catalog;
2031          if (ua->catalog && ua->catalog != catalog) {
2032             close_db(ua);
2033          }
2034          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2035             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2036             return false;
2037          }
2038          ua->catalog = catalog;
2039          return open_db(ua);
2040       }
2041    }
2042
2043    /* Try for Job keyword */
2044    i = find_arg_with_value(ua, NT_("job"));
2045    if (i >= 0) {
2046       if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2047          ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2048          return false;
2049       }
2050       job = GetJobResWithName(ua->argv[i]);
2051       if (job) {
2052          catalog = job->client->catalog;
2053          if (ua->catalog && ua->catalog != catalog) {
2054             close_db(ua);
2055          }
2056          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2057             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2058             return false;
2059          }
2060          ua->catalog = catalog;
2061          return open_db(ua);
2062       }
2063    }
2064
2065    return open_db(ua);
2066 }
2067
2068
2069 /*
2070  * Open the catalog database.
2071  */
2072 bool open_db(UAContext *ua)
2073 {
2074    bool mult_db_conn;
2075
2076    if (ua->db) {
2077       return true;
2078    }
2079    if (!ua->catalog) {
2080       ua->catalog = get_catalog_resource(ua);
2081       if (!ua->catalog) {
2082          ua->error_msg( _("Could not find a Catalog resource\n"));
2083          return false;
2084       }
2085    }
2086
2087    /* Some modules like bvfs need their own catalog connection */
2088    mult_db_conn = ua->catalog->mult_db_connections;
2089    if (ua->force_mult_db_connections) {
2090       mult_db_conn = true;
2091    }
2092
2093    ua->jcr->catalog = ua->catalog;
2094
2095    Dmsg0(100, "UA Open database\n");
2096    ua->db = db_init_database(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name, 
2097                              ua->catalog->db_user,
2098                              ua->catalog->db_password, ua->catalog->db_address,
2099                              ua->catalog->db_port, ua->catalog->db_socket,
2100                              mult_db_conn, ua->catalog->disable_batch_insert);
2101    if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2102       ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2103                  ua->catalog->db_name);
2104       if (ua->db) {
2105          ua->error_msg("%s", db_strerror(ua->db));
2106       }
2107       close_db(ua);
2108       return false;
2109    }
2110    ua->jcr->db = ua->db;
2111    if (!ua->api) {
2112       ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name()); 
2113    }
2114    Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2115    return true;
2116 }
2117
2118 void close_db(UAContext *ua)
2119 {
2120    if (ua->db) {
2121       db_close_database(ua->jcr, ua->db);
2122       ua->db = NULL;
2123       if (ua->jcr) {
2124          ua->jcr->db = NULL;
2125       }
2126    }
2127 }