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