]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_cmds.c
replace set_Jobxxx by setJobxxx
[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 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
876 {
877    BSOCK *sd;
878    JCR *jcr = ua->jcr;
879    USTORE lstore;
880    
881    lstore.store = store;
882    pm_strcpy(lstore.store_source, _("unknown source"));
883    set_wstorage(jcr, &lstore);
884    /* Try connecting for up to 15 seconds */
885    ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
886       store->name(), store->address, store->SDport);
887    if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
888       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
889       return;
890    }
891    Dmsg0(120, _("Connected to storage daemon\n"));
892    sd = jcr->store_bsock;
893    sd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
894    if (sd->recv() >= 0) {
895       ua->send_msg("%s", sd->msg);
896    }
897    sd->signal(BNET_TERMINATE);
898    sd->close();
899    jcr->store_bsock = NULL;
900    return;
901 }
902
903 /*
904  * For the client, we have the following values that can be set
905  *  level = debug level
906  *  trace = send debug output to a file
907  *  hangup = how many records to send to SD before hanging up
908  *    obviously this is most useful for testing restarting
909  *    failed jobs.
910  */
911 static void do_client_setdebug(UAContext *ua, CLIENT *client, 
912               int level, int trace, int hangup)
913 {
914    BSOCK *fd;
915
916    /* Connect to File daemon */
917
918    ua->jcr->client = client;
919    /* Try to connect for 15 seconds */
920    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
921       client->name(), client->address, client->FDport);
922    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
923       ua->error_msg(_("Failed to connect to Client.\n"));
924       return;
925    }
926    Dmsg0(120, "Connected to file daemon\n");
927    fd = ua->jcr->file_bsock;
928    fd->fsend("setdebug=%d trace=%d hangup=%d\n", level, trace, hangup);
929    if (fd->recv() >= 0) {
930       ua->send_msg("%s", fd->msg);
931    }
932    fd->signal(BNET_TERMINATE);
933    fd->close();
934    ua->jcr->file_bsock = NULL;
935    return;
936 }
937
938
939 static void do_all_setdebug(UAContext *ua, int level, int trace_flag, int hangup)
940 {
941    STORE *store, **unique_store;
942    CLIENT *client, **unique_client;
943    int i, j, found;
944
945    /* Director */
946    debug_level = level;
947
948    /* Count Storage items */
949    LockRes();
950    store = NULL;
951    i = 0;
952    foreach_res(store, R_STORAGE) {
953       i++;
954    }
955    unique_store = (STORE **) malloc(i * sizeof(STORE));
956    /* Find Unique Storage address/port */
957    store = (STORE *)GetNextRes(R_STORAGE, NULL);
958    i = 0;
959    unique_store[i++] = store;
960    while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
961       found = 0;
962       for (j=0; j<i; j++) {
963          if (strcmp(unique_store[j]->address, store->address) == 0 &&
964              unique_store[j]->SDport == store->SDport) {
965             found = 1;
966             break;
967          }
968       }
969       if (!found) {
970          unique_store[i++] = store;
971          Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
972       }
973    }
974    UnlockRes();
975
976    /* Call each unique Storage daemon */
977    for (j=0; j<i; j++) {
978       do_storage_setdebug(ua, unique_store[j], level, trace_flag);
979    }
980    free(unique_store);
981
982    /* Count Client items */
983    LockRes();
984    client = NULL;
985    i = 0;
986    foreach_res(client, R_CLIENT) {
987       i++;
988    }
989    unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
990    /* Find Unique Client address/port */
991    client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
992    i = 0;
993    unique_client[i++] = client;
994    while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
995       found = 0;
996       for (j=0; j<i; j++) {
997          if (strcmp(unique_client[j]->address, client->address) == 0 &&
998              unique_client[j]->FDport == client->FDport) {
999             found = 1;
1000             break;
1001          }
1002       }
1003       if (!found) {
1004          unique_client[i++] = client;
1005          Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
1006       }
1007    }
1008    UnlockRes();
1009
1010    /* Call each unique File daemon */
1011    for (j=0; j<i; j++) {
1012       do_client_setdebug(ua, unique_client[j], level, trace_flag, hangup);
1013    }
1014    free(unique_client);
1015 }
1016
1017 /*
1018  * setdebug level=nn all trace=1/0
1019  */
1020 static int setdebug_cmd(UAContext *ua, const char *cmd)
1021 {
1022    STORE *store;
1023    CLIENT *client;
1024    int level;
1025    int trace_flag = -1;
1026    int hangup = -1;
1027    int i;
1028
1029    Dmsg1(120, "setdebug:%s:\n", cmd);
1030
1031    level = -1;
1032    i = find_arg_with_value(ua, "level");
1033    if (i >= 0) {
1034       level = atoi(ua->argv[i]);
1035    }
1036    if (level < 0) {
1037       if (!get_pint(ua, _("Enter new debug level: "))) {
1038          return 1;
1039       }
1040       level = ua->pint32_val;
1041    }
1042
1043    /* Look for trace flag. -1 => not change */
1044    i = find_arg_with_value(ua, "trace");
1045    if (i >= 0) {
1046       trace_flag = atoi(ua->argv[i]);
1047       if (trace_flag > 0) {
1048          trace_flag = 1;
1049       }
1050    }
1051
1052    /* Look for hangup (debug only)flag. -1 => not change */
1053    i = find_arg_with_value(ua, "hangup");
1054    if (i >= 0) {
1055       hangup = atoi(ua->argv[i]);
1056    }
1057
1058
1059    /* General debug? */
1060    for (i=1; i<ua->argc; i++) {
1061       if (strcasecmp(ua->argk[i], "all") == 0) {
1062          do_all_setdebug(ua, level, trace_flag, hangup);
1063          return 1;
1064       }
1065       if (strcasecmp(ua->argk[i], "dir") == 0 ||
1066           strcasecmp(ua->argk[i], "director") == 0) {
1067          debug_level = level;
1068          set_trace(trace_flag);
1069          return 1;
1070       }
1071       if (strcasecmp(ua->argk[i], "client") == 0 ||
1072           strcasecmp(ua->argk[i], "fd") == 0) {
1073          client = NULL;
1074          if (ua->argv[i]) {
1075             client = GetClientResWithName(ua->argv[i]);
1076             if (client) {
1077                do_client_setdebug(ua, client, level, trace_flag, hangup);
1078                return 1;
1079             }
1080          }
1081          client = select_client_resource(ua);
1082          if (client) {
1083             do_client_setdebug(ua, client, level, trace_flag, hangup);
1084             return 1;
1085          }
1086       }
1087
1088       if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1089           strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1090           strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1091          store = NULL;
1092          if (ua->argv[i]) {
1093             store = GetStoreResWithName(ua->argv[i]);
1094             if (store) {
1095                do_storage_setdebug(ua, store, level, trace_flag);
1096                return 1;
1097             }
1098          }
1099          store = get_storage_resource(ua, false/*no default*/);
1100          if (store) {
1101             do_storage_setdebug(ua, store, level, trace_flag);
1102             return 1;
1103          }
1104       }
1105    }
1106    /*
1107     * We didn't find an appropriate keyword above, so
1108     * prompt the user.
1109     */
1110    start_prompt(ua, _("Available daemons are: \n"));
1111    add_prompt(ua, _("Director"));
1112    add_prompt(ua, _("Storage"));
1113    add_prompt(ua, _("Client"));
1114    add_prompt(ua, _("All"));
1115    switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1116    case 0:                         /* Director */
1117       debug_level = level;
1118       set_trace(trace_flag);
1119       break;
1120    case 1:
1121       store = get_storage_resource(ua, false/*no default*/);
1122       if (store) {
1123          do_storage_setdebug(ua, store, level, trace_flag);
1124       }
1125       break;
1126    case 2:
1127       client = select_client_resource(ua);
1128       if (client) {
1129          do_client_setdebug(ua, client, level, trace_flag, hangup);
1130       }
1131       break;
1132    case 3:
1133       do_all_setdebug(ua, level, trace_flag, hangup);
1134       break;
1135    default:
1136       break;
1137    }
1138    return 1;
1139 }
1140
1141 /*
1142  * Turn debug tracing to file on/off
1143  */
1144 static int trace_cmd(UAContext *ua, const char *cmd)
1145 {
1146    char *onoff;
1147
1148    if (ua->argc != 2) {
1149       if (!get_cmd(ua, _("Turn on or off? "))) {
1150             return 1;
1151       }
1152       onoff = ua->cmd;
1153    } else {
1154       onoff = ua->argk[1];
1155    }
1156
1157    set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1158    return 1;
1159
1160 }
1161
1162 static int var_cmd(UAContext *ua, const char *cmd)
1163 {
1164    POOLMEM *val = get_pool_memory(PM_FNAME);
1165    char *var;
1166
1167    if (!open_client_db(ua)) {
1168       return 1;
1169    }
1170    for (var=ua->cmd; *var != ' '; ) {    /* skip command */
1171       var++;
1172    }
1173    while (*var == ' ') {                 /* skip spaces */
1174       var++;
1175    }
1176    Dmsg1(100, "Var=%s:\n", var);
1177    variable_expansion(ua->jcr, var, &val);
1178    ua->send_msg("%s\n", val);
1179    free_pool_memory(val);
1180    return 1;
1181 }
1182
1183 static int estimate_cmd(UAContext *ua, const char *cmd)
1184 {
1185    JOB *job = NULL;
1186    CLIENT *client = NULL;
1187    FILESET *fileset = NULL;
1188    int listing = 0;
1189    char since[MAXSTRING];
1190    JCR *jcr = ua->jcr;
1191    int accurate=-1;
1192
1193    jcr->setJobLevel(L_FULL);
1194    for (int i=1; i<ua->argc; i++) {
1195       if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1196           strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1197          if (ua->argv[i]) {
1198             client = GetClientResWithName(ua->argv[i]);
1199             if (!client) {
1200                ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1201                return 1;
1202             }
1203             if (!acl_access_ok(ua, Client_ACL, client->name())) {
1204                ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1205                return 1;
1206             }
1207             continue;
1208          } else {
1209             ua->error_msg(_("Client name missing.\n"));
1210             return 1;
1211          }
1212       }
1213       if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1214          if (ua->argv[i]) {
1215             job = GetJobResWithName(ua->argv[i]);
1216             if (!job) {
1217                ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1218                return 1;
1219             }
1220             if (!acl_access_ok(ua, Job_ACL, job->name())) {
1221                ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1222                return 1;
1223             }
1224             continue;
1225          } else {
1226             ua->error_msg(_("Job name missing.\n"));
1227             return 1;
1228          }
1229
1230       }
1231       if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1232          if (ua->argv[i]) {
1233             fileset = GetFileSetResWithName(ua->argv[i]);
1234             if (!fileset) {
1235                ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1236                return 1;
1237             }
1238             if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1239                ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1240                return 1;
1241             }
1242             continue;
1243          } else {
1244             ua->error_msg(_("Fileset name missing.\n"));
1245             return 1;
1246          }
1247       }
1248       if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1249          listing = 1;
1250          continue;
1251       }
1252       if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1253          if (ua->argv[i]) {
1254             if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1255                ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1256             }
1257             continue;
1258          } else {
1259            ua->error_msg(_("Level value missing.\n"));
1260            return 1;
1261          }
1262       }
1263       if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1264          if (!is_yesno(ua->argv[i], &accurate)) {
1265             ua->error_msg(_("Invalid value for accurate. " 
1266                             "It must be yes or no.\n"));
1267          }
1268       }
1269    }
1270    if (!job && !(client && fileset)) {
1271       if (!(job = select_job_resource(ua))) {
1272          return 1;
1273       }
1274    }
1275    if (!job) {
1276       job = GetJobResWithName(ua->argk[1]);
1277       if (!job) {
1278          ua->error_msg(_("No job specified.\n"));
1279          return 1;
1280       }
1281       if (!acl_access_ok(ua, Job_ACL, job->name())) {
1282          ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1283          return 1;
1284       }
1285    }
1286    if (!client) {
1287       client = job->client;
1288    }
1289    if (!fileset) {
1290       fileset = job->fileset;
1291    }
1292    jcr->client = client;
1293    jcr->fileset = fileset;
1294    close_db(ua);
1295    if (job->pool->catalog) {
1296       ua->catalog = job->pool->catalog;
1297    } else {
1298       ua->catalog = client->catalog;
1299    }
1300
1301    if (!open_db(ua)) {
1302       return 1;
1303    }
1304
1305    jcr->job = job;
1306    jcr->setJobType(JT_BACKUP);
1307    init_jcr_job_record(jcr);
1308
1309    if (!get_or_create_client_record(jcr)) {
1310       return 1;
1311    }
1312    if (!get_or_create_fileset_record(jcr)) {
1313       return 1;
1314    }
1315
1316    get_level_since_time(ua->jcr, since, sizeof(since));
1317
1318    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1319       jcr->client->name(), jcr->client->address, jcr->client->FDport);
1320    if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1321       ua->error_msg(_("Failed to connect to Client.\n"));
1322       return 1;
1323    }
1324
1325    if (!send_include_list(jcr)) {
1326       ua->error_msg(_("Error sending include list.\n"));
1327       goto bail_out;
1328    }
1329
1330    if (!send_exclude_list(jcr)) {
1331       ua->error_msg(_("Error sending exclude list.\n"));
1332       goto bail_out;
1333    }
1334
1335    /* The level string change if accurate mode is enabled */
1336    if (accurate >= 0) {
1337       jcr->accurate = accurate;
1338    } else {
1339       jcr->accurate = job->accurate;
1340    }
1341
1342    if (!send_level_command(jcr)) {
1343       goto bail_out;
1344    }
1345
1346    /*
1347     * If the job is in accurate mode, we send the list of
1348     * all files to FD.
1349     */
1350    Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1351    if (!send_accurate_current_files(jcr)) {
1352       goto bail_out;
1353    }
1354
1355    jcr->file_bsock->fsend("estimate listing=%d\n", listing);
1356    while (jcr->file_bsock->recv() >= 0) {
1357       ua->send_msg("%s", jcr->file_bsock->msg);
1358    }
1359
1360 bail_out:
1361    if (jcr->file_bsock) {
1362       jcr->file_bsock->signal(BNET_TERMINATE);
1363       jcr->file_bsock->close();
1364       jcr->file_bsock = NULL;
1365    }
1366    return 1;
1367 }
1368
1369
1370 /*
1371  * print time
1372  */
1373 static int time_cmd(UAContext *ua, const char *cmd)
1374 {
1375    char sdt[50];
1376    time_t ttime = time(NULL);
1377    struct tm tm;
1378    (void)localtime_r(&ttime, &tm);
1379    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1380    ua->send_msg("%s\n", sdt);
1381    return 1;
1382 }
1383
1384 /*
1385  * reload the conf file
1386  */
1387 extern "C" void reload_config(int sig);
1388
1389 static int reload_cmd(UAContext *ua, const char *cmd)
1390 {
1391    reload_config(1);
1392    return 1;
1393 }
1394
1395 /*
1396  * Delete Pool records (should purge Media with it).
1397  *
1398  *  delete pool=<pool-name>
1399  *  delete volume pool=<pool-name> volume=<name>
1400  *  delete jobid=xxx
1401  */
1402 static int delete_cmd(UAContext *ua, const char *cmd)
1403 {
1404    static const char *keywords[] = {
1405       NT_("volume"),
1406       NT_("pool"),
1407       NT_("jobid"),
1408       NULL};
1409
1410    if (!open_client_db(ua)) {
1411       return 1;
1412    }
1413
1414    switch (find_arg_keyword(ua, keywords)) {
1415    case 0:
1416       delete_volume(ua);
1417       return 1;
1418    case 1:
1419       delete_pool(ua);
1420       return 1;
1421    case 2:
1422       int i;
1423       while ((i=find_arg(ua, "jobid")) > 0) {
1424          delete_job(ua);
1425          *ua->argk[i] = 0;         /* zap keyword already visited */
1426       }
1427       return 1;
1428    default:
1429       break;
1430    }
1431
1432    ua->warning_msg(_(
1433 "In general it is not a good idea to delete either a\n"
1434 "Pool or a Volume since they may contain data.\n\n"));
1435
1436    switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1437    case 0:
1438       delete_volume(ua);
1439       break;
1440    case 1:
1441       delete_pool(ua);
1442       break;
1443    case 2:
1444       delete_job(ua);
1445       return 1;
1446    default:
1447       ua->warning_msg(_("Nothing done.\n"));
1448       break;
1449    }
1450    return 1;
1451 }
1452
1453
1454 /*
1455  * delete_job has been modified to parse JobID lists like the
1456  * following:
1457  * delete JobID=3,4,6,7-11,14
1458  *
1459  * Thanks to Phil Stracchino for the above addition.
1460  */
1461
1462 static void delete_job(UAContext *ua)
1463 {
1464    JobId_t JobId;
1465    char *s,*sep,*tok;
1466
1467    int i = find_arg_with_value(ua, NT_("jobid"));
1468    if (i >= 0) {
1469       if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1470         s = bstrdup(ua->argv[i]);
1471         tok = s;
1472         /*
1473          * We could use strtok() here.  But we're not going to, because:
1474          * (a) strtok() is deprecated, having been replaced by strsep();
1475          * (b) strtok() is broken in significant ways.
1476          * we could use strsep() instead, but it's not universally available.
1477          * so we grow our own using strchr().
1478          */
1479         sep = strchr(tok, ',');
1480         while (sep != NULL) {
1481            *sep = '\0';
1482            if (!delete_job_id_range(ua, tok)) {
1483               JobId = str_to_int64(tok);
1484               do_job_delete(ua, JobId);
1485            }
1486            tok = ++sep;
1487            sep = strchr(tok, ',');
1488         }
1489         /* pick up the last token */
1490         if (!delete_job_id_range(ua, tok)) {
1491             JobId = str_to_int64(tok);
1492             do_job_delete(ua, JobId);
1493         }
1494
1495          free(s);
1496       } else {
1497          JobId = str_to_int64(ua->argv[i]);
1498         do_job_delete(ua, JobId);
1499       }
1500    } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1501       return;
1502    } else {
1503       JobId = ua->int64_val;
1504       do_job_delete(ua, JobId);
1505    }
1506 }
1507
1508 /*
1509  * we call delete_job_id_range to parse range tokens and iterate over ranges
1510  */
1511 static bool delete_job_id_range(UAContext *ua, char *tok)
1512 {
1513    char *tok2;
1514    JobId_t j,j1,j2;
1515
1516    tok2 = strchr(tok, '-');
1517    if (!tok2) {
1518       return false;
1519    }
1520    *tok2 = '\0';
1521    tok2++;
1522    j1 = str_to_int64(tok);
1523    j2 = str_to_int64(tok2);
1524    for (j=j1; j<=j2; j++) {
1525       do_job_delete(ua, j);
1526    }
1527    return true;
1528 }
1529
1530 /*
1531  * do_job_delete now performs the actual delete operation atomically
1532  */
1533 static void do_job_delete(UAContext *ua, JobId_t JobId)
1534 {
1535    char ed1[50];
1536
1537    edit_int64(JobId, ed1);
1538    purge_jobs_from_catalog(ua, ed1);
1539    ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1540 }
1541
1542 /*
1543  * Delete media records from database -- dangerous
1544  */
1545 static int delete_volume(UAContext *ua)
1546 {
1547    MEDIA_DBR mr;
1548    char buf[1000];
1549    db_list_ctx lst;
1550
1551    if (!select_media_dbr(ua, &mr)) {
1552       return 1;
1553    }
1554    ua->warning_msg(_("\nThis command will delete volume %s\n"
1555       "and all Jobs saved on that volume from the Catalog\n"),
1556       mr.VolumeName);
1557
1558    if (find_arg(ua, "yes") >= 0) {
1559       ua->pint32_val = 1; /* Have "yes" on command line already" */
1560    } else {
1561       bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1562          mr.VolumeName);
1563       if (!get_yesno(ua, buf)) {
1564          return 1;
1565       }
1566    }
1567    if (!ua->pint32_val) {
1568       return 1;
1569    }
1570
1571    /* If not purged, do it */
1572    if (strcmp(mr.VolStatus, "Purged") != 0) {
1573       if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1574          ua->error_msg(_("Can't list jobs on this volume\n"));
1575          return 1;
1576       }
1577       if (lst.count) {
1578          purge_jobs_from_catalog(ua, lst.list);
1579       }
1580    }
1581
1582    db_delete_media_record(ua->jcr, ua->db, &mr);
1583    return 1;
1584 }
1585
1586 /*
1587  * Delete a pool record from the database -- dangerous
1588  */
1589 static int delete_pool(UAContext *ua)
1590 {
1591    POOL_DBR  pr;
1592    char buf[200];
1593
1594    memset(&pr, 0, sizeof(pr));
1595
1596    if (!get_pool_dbr(ua, &pr)) {
1597       return 1;
1598    }
1599    bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1600       pr.Name);
1601    if (!get_yesno(ua, buf)) {
1602       return 1;
1603    }
1604    if (ua->pint32_val) {
1605       db_delete_pool_record(ua->jcr, ua->db, &pr);
1606    }
1607    return 1;
1608 }
1609
1610 int memory_cmd(UAContext *ua, const char *cmd)
1611 {
1612    garbage_collect_memory();
1613    list_dir_status_header(ua);
1614    sm_dump(false, true);
1615    return 1;
1616 }
1617
1618 static void do_mount_cmd(UAContext *ua, const char *command)
1619 {
1620    USTORE store;
1621    BSOCK *sd;
1622    JCR *jcr = ua->jcr;
1623    char dev_name[MAX_NAME_LENGTH];
1624    int drive;
1625    int slot = -1;
1626
1627    if (!open_client_db(ua)) {
1628       return;
1629    }
1630    Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1631
1632    store.store = get_storage_resource(ua, true/*arg is storage*/);
1633    if (!store.store) {
1634       return;
1635    }
1636    pm_strcpy(store.store_source, _("unknown source"));
1637    set_wstorage(jcr, &store);
1638    drive = get_storage_drive(ua, store.store);
1639    if (strcmp(command, "mount") == 0) {
1640       slot = get_storage_slot(ua, store.store);
1641    }
1642
1643    Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1644       store.store->media_type, store.store->dev_name(), drive);
1645
1646    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1647       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1648       return;
1649    }
1650    sd = jcr->store_bsock;
1651    bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1652    bash_spaces(dev_name);
1653    if (slot > 0) {
1654       sd->fsend("%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1655    } else {
1656       sd->fsend("%s %s drive=%d", command, dev_name, drive);
1657    }
1658    while (sd->recv() >= 0) {
1659       ua->send_msg("%s", sd->msg);
1660    }
1661    sd->signal(BNET_TERMINATE);
1662    sd->close();
1663    jcr->store_bsock = NULL;
1664 }
1665
1666 /*
1667  * mount [storage=<name>] [drive=nn] [slot=mm]
1668  */
1669 static int mount_cmd(UAContext *ua, const char *cmd)
1670 {
1671    do_mount_cmd(ua, "mount");          /* mount */
1672    return 1;
1673 }
1674
1675
1676 /*
1677  * unmount [storage=<name>] [drive=nn]
1678  */
1679 static int unmount_cmd(UAContext *ua, const char *cmd)
1680 {
1681    do_mount_cmd(ua, "unmount");          /* unmount */
1682    return 1;
1683 }
1684
1685
1686 /*
1687  * release [storage=<name>] [drive=nn]
1688  */
1689 static int release_cmd(UAContext *ua, const char *cmd)
1690 {
1691    do_mount_cmd(ua, "release");          /* release */
1692    return 1;
1693 }
1694
1695
1696 /*
1697  * Switch databases
1698  *   use catalog=<name>
1699  */
1700 static int use_cmd(UAContext *ua, const char *cmd)
1701 {
1702    CAT *oldcatalog, *catalog;
1703
1704
1705    close_db(ua);                      /* close any previously open db */
1706    oldcatalog = ua->catalog;
1707
1708    if (!(catalog = get_catalog_resource(ua))) {
1709       ua->catalog = oldcatalog;
1710    } else {
1711       ua->catalog = catalog;
1712    }
1713    if (open_db(ua)) {
1714       ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1715          ua->catalog->name(), ua->catalog->db_name);
1716    }
1717    return 1;
1718 }
1719
1720 int quit_cmd(UAContext *ua, const char *cmd)
1721 {
1722    ua->quit = true;
1723    return 1;
1724 }
1725
1726 /* Handler to get job status */
1727 static int status_handler(void *ctx, int num_fields, char **row)
1728 {
1729    char *val = (char *)ctx;
1730
1731    if (row[0]) {
1732       *val = row[0][0];
1733    } else {
1734       *val = '?';               /* Unknown by default */
1735    }
1736
1737    return 0;
1738 }
1739
1740 /*
1741  * Wait until no job is running
1742  */
1743 int wait_cmd(UAContext *ua, const char *cmd)
1744 {
1745    JCR *jcr;
1746    int i;
1747    time_t stop_time = 0;
1748
1749    /*
1750     * no args
1751     * Wait until no job is running
1752     */
1753    if (ua->argc == 1) {
1754       bmicrosleep(0, 200000);            /* let job actually start */
1755       for (bool running=true; running; ) {
1756          running = false;
1757          foreach_jcr(jcr) {
1758             if (jcr->JobId != 0) {
1759                running = true;
1760                break;
1761             }
1762          }
1763          endeach_jcr(jcr);
1764
1765          if (running) {
1766             bmicrosleep(1, 0);
1767          }
1768       }
1769       return 1;
1770    }
1771
1772    i = find_arg_with_value(ua, NT_("timeout")); 
1773    if (i > 0 && ua->argv[i]) {
1774       stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1775    }
1776
1777    /* we have jobid, jobname or ujobid argument */
1778
1779    uint32_t jobid = 0 ;
1780
1781    if (!open_client_db(ua)) {
1782       ua->error_msg(_("ERR: Can't open db\n")) ;
1783       return 1;
1784    }
1785
1786    for (int i=1; i<ua->argc; i++) {
1787       if (strcasecmp(ua->argk[i], "jobid") == 0) {
1788          if (!ua->argv[i]) {
1789             break;
1790          }
1791          jobid = str_to_int64(ua->argv[i]);
1792          break;
1793       } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1794                  strcasecmp(ua->argk[i], "job") == 0) {
1795          if (!ua->argv[i]) {
1796             break;
1797          }
1798          jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1799          if (jcr) {
1800             jobid = jcr->JobId ;
1801             free_jcr(jcr);
1802          }
1803          break;
1804       } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1805          if (!ua->argv[i]) {
1806             break;
1807          }
1808          jcr=get_jcr_by_full_name(ua->argv[i]) ;
1809          if (jcr) {
1810             jobid = jcr->JobId ;
1811             free_jcr(jcr);
1812          }
1813          break;
1814       /* Wait for a mount request */
1815       } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1816          for (bool waiting=false; !waiting; ) {
1817             foreach_jcr(jcr) {
1818                if (jcr->JobId != 0 && 
1819                    (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1820                   waiting = true;
1821                   break;
1822                }
1823             }
1824             endeach_jcr(jcr);
1825             if (waiting) {
1826                break;
1827             }
1828             if (stop_time && (time(NULL) >= stop_time)) {
1829                ua->warning_msg(_("Wait on mount timed out\n"));
1830                return 1;
1831             }
1832             bmicrosleep(1, 0);
1833          }
1834          return 1;
1835       }
1836    }
1837
1838    if (jobid == 0) {
1839       ua->error_msg(_("ERR: Job was not found\n"));
1840       return 1 ;
1841    }
1842
1843    /*
1844     * We wait the end of a specific job
1845     */
1846
1847    bmicrosleep(0, 200000);            /* let job actually start */
1848    for (bool running=true; running; ) {
1849       running = false;
1850
1851       jcr=get_jcr_by_id(jobid) ;
1852
1853       if (jcr) {
1854          running = true ;
1855          free_jcr(jcr);
1856       }
1857
1858       if (running) {
1859          bmicrosleep(1, 0);
1860       }
1861    }
1862
1863    /*
1864     * We have to get JobStatus
1865     */
1866
1867    int status ;
1868    char jobstatus = '?';        /* Unknown by default */
1869    char buf[256] ;
1870
1871    bsnprintf(buf, sizeof(buf),
1872              "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1873
1874
1875    db_sql_query(ua->db, buf,
1876                 status_handler, (void *)&jobstatus);
1877
1878    switch (jobstatus) {
1879    case JS_Error:
1880       status = 1 ;         /* Warning */
1881       break;
1882
1883    case JS_FatalError:
1884    case JS_ErrorTerminated:
1885    case JS_Canceled:
1886       status = 2 ;         /* Critical */
1887       break;
1888
1889    case JS_Warnings:
1890    case JS_Terminated:
1891       status = 0 ;         /* Ok */
1892       break;
1893
1894    default:
1895       status = 3 ;         /* Unknown */
1896       break;
1897    }
1898
1899    ua->send_msg("JobId=%i\n", jobid) ;
1900    ua->send_msg("JobStatus=%s (%c)\n", 
1901             job_status_to_str(jobstatus), 
1902             jobstatus) ;
1903
1904    if (ua->gui || ua->api) {
1905       ua->send_msg("ExitStatus=%i\n", status) ;
1906    }
1907
1908    return 1;
1909 }
1910
1911
1912 static int help_cmd(UAContext *ua, const char *cmd)
1913 {
1914    int i;
1915    ua->send_msg(_("  Command       Description\n  =======       ===========\n"));
1916    for (i=0; i<comsize; i++) {
1917       if (ua->argc == 2) {
1918          if (!strcasecmp(ua->argk[1], commands[i].key)) {
1919             ua->send_msg(_("  %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key, 
1920                          commands[i].help, commands[i].usage);
1921             break;
1922          }
1923       } else {
1924          ua->send_msg(_("  %-13s %s\n"), commands[i].key, commands[i].help);
1925       }
1926    }
1927    if (i == comsize && ua->argc == 2) {
1928       ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
1929    }
1930    ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1931    return 1;
1932 }
1933
1934 int qhelp_cmd(UAContext *ua, const char *cmd)
1935 {
1936    int i,j;
1937    /* Want to display only commands */
1938    j = find_arg(ua, NT_("all"));
1939    if (j >= 0) {
1940       for (i=0; i<comsize; i++) {
1941          ua->send_msg("%s\n", commands[i].key);
1942       }
1943       return 1;
1944    }
1945    /* Want to display a specific help section */
1946    j = find_arg_with_value(ua, NT_("item"));
1947    if (j >= 0 && ua->argk[j]) {
1948       for (i=0; i<comsize; i++) {
1949          if (bstrcmp(commands[i].key, ua->argv[j])) {
1950             ua->send_msg("%s\n", commands[i].usage);
1951             break;
1952          }
1953       }
1954       return 1;
1955    }
1956    /* Want to display everything */
1957    for (i=0; i<comsize; i++) {
1958       ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
1959    }
1960    return 1;
1961 }
1962
1963 #if 1 
1964 static int version_cmd(UAContext *ua, const char *cmd)
1965 {
1966    ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
1967                 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
1968    return 1;
1969 }
1970 #else
1971 /*
1972  *  Test code -- turned on only for debug testing 
1973  */
1974 static int version_cmd(UAContext *ua, const char *cmd)
1975 {
1976    dbid_list ids;
1977    POOL_MEM query(PM_MESSAGE);
1978    open_db(ua);
1979    Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1980    db_get_query_dbids(ua->jcr, ua->db, query, ids);
1981    ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1982    for (int i=0; i < ids.num_ids; i++) {
1983       ua->send_msg("id=%d\n", ids.DBId[i]);
1984    }
1985    close_db(ua);
1986    return 1;
1987 }
1988 #endif
1989
1990 /* 
1991  * This call uses open_client_db() and force a
1992  * new dedicated connection to the catalog
1993  */
1994 bool open_new_client_db(UAContext *ua)
1995 {   
1996    bool ret;
1997
1998    /* Force a new dedicated connection */
1999    close_db(ua);
2000    ua->force_mult_db_connections = true;
2001    ret = open_client_db(ua);
2002    ua->force_mult_db_connections = false;
2003    return ret;
2004 }
2005
2006 /* 
2007  * This call explicitly checks for a catalog=xxx and
2008  *  if given, opens that catalog.  It also checks for
2009  *  client=xxx and if found, opens the catalog 
2010  *  corresponding to that client. If we still don't 
2011  *  have a catalog, look for a Job keyword and get the
2012  *  catalog from its client record.
2013  */
2014 bool open_client_db(UAContext *ua)
2015 {
2016    int i;
2017    CAT *catalog;
2018    CLIENT *client;
2019    JOB *job;
2020
2021    /* Try for catalog keyword */
2022    i = find_arg_with_value(ua, NT_("catalog"));
2023    if (i >= 0) {
2024       if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
2025          ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
2026          return false;
2027       }
2028       catalog = GetCatalogResWithName(ua->argv[i]);
2029       if (catalog) {
2030          if (ua->catalog && ua->catalog != catalog) {
2031             close_db(ua);
2032          }
2033          ua->catalog = catalog;
2034          return open_db(ua);
2035       }
2036    }
2037
2038    /* Try for client keyword */
2039    i = find_arg_with_value(ua, NT_("client"));
2040    if (i >= 0) {
2041       if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
2042          ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2043          return false;
2044       }
2045       client = GetClientResWithName(ua->argv[i]);
2046       if (client) {
2047          catalog = client->catalog;
2048          if (ua->catalog && ua->catalog != catalog) {
2049             close_db(ua);
2050          }
2051          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2052             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2053             return false;
2054          }
2055          ua->catalog = catalog;
2056          return open_db(ua);
2057       }
2058    }
2059
2060    /* Try for Job keyword */
2061    i = find_arg_with_value(ua, NT_("job"));
2062    if (i >= 0) {
2063       if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2064          ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2065          return false;
2066       }
2067       job = GetJobResWithName(ua->argv[i]);
2068       if (job) {
2069          catalog = job->client->catalog;
2070          if (ua->catalog && ua->catalog != catalog) {
2071             close_db(ua);
2072          }
2073          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2074             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2075             return false;
2076          }
2077          ua->catalog = catalog;
2078          return open_db(ua);
2079       }
2080    }
2081
2082    return open_db(ua);
2083 }
2084
2085
2086 /*
2087  * Open the catalog database.
2088  */
2089 bool open_db(UAContext *ua)
2090 {
2091    bool mult_db_conn;
2092
2093    if (ua->db) {
2094       return true;
2095    }
2096    if (!ua->catalog) {
2097       ua->catalog = get_catalog_resource(ua);
2098       if (!ua->catalog) {
2099          ua->error_msg( _("Could not find a Catalog resource\n"));
2100          return false;
2101       }
2102    }
2103
2104    /* Some modules like bvfs need their own catalog connection */
2105    mult_db_conn = ua->catalog->mult_db_connections;
2106    if (ua->force_mult_db_connections) {
2107       mult_db_conn = true;
2108    }
2109
2110    ua->jcr->catalog = ua->catalog;
2111
2112    Dmsg0(100, "UA Open database\n");
2113    ua->db = db_init_database(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name, 
2114                              ua->catalog->db_user,
2115                              ua->catalog->db_password, ua->catalog->db_address,
2116                              ua->catalog->db_port, ua->catalog->db_socket,
2117                              mult_db_conn, ua->catalog->disable_batch_insert);
2118    if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2119       ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2120                  ua->catalog->db_name);
2121       if (ua->db) {
2122          ua->error_msg("%s", db_strerror(ua->db));
2123       }
2124       close_db(ua);
2125       return false;
2126    }
2127    ua->jcr->db = ua->db;
2128    if (!ua->api) {
2129       ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name()); 
2130    }
2131    Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2132    return true;
2133 }
2134
2135 void close_db(UAContext *ua)
2136 {
2137    if (ua->db) {
2138       db_close_database(ua->jcr, ua->db);
2139       ua->db = NULL;
2140       if (ua->jcr) {
2141          ua->jcr->db = NULL;
2142       }
2143    }
2144 }