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