]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_cmds.c
fb64298466bae6ed268d05d3c41db0751758ffed
[bacula/bacula] / bacula / src / dird / ua_cmds.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2012 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   Bacula Director -- User Agent Commands
31  *
32  *     Kern Sibbald, September MM
33  *
34  */
35  
36 #include "bacula.h"
37 #include "dird.h"
38
39 #ifdef HAVE_PYTHON
40
41 #undef _POSIX_C_SOURCE
42 #include <Python.h>
43
44 #include "lib/pythonlib.h"
45
46 /* Imported Functions */
47 extern PyObject *job_getattr(PyObject *self, char *attrname);
48 extern int job_setattr(PyObject *self, char *attrname, PyObject *value);
49
50 #endif /* HAVE_PYTHON */
51
52 /* Imported subroutines */
53
54 /* Imported variables */
55 extern jobq_t job_queue;              /* job queue */
56
57
58 /* Imported functions */
59 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
60 extern int gui_cmd(UAContext *ua, const char *cmd);
61 extern int label_cmd(UAContext *ua, const char *cmd);
62 extern int list_cmd(UAContext *ua, const char *cmd);
63 extern int llist_cmd(UAContext *ua, const char *cmd);
64 extern int messagescmd(UAContext *ua, const char *cmd);
65 extern int prunecmd(UAContext *ua, const char *cmd);
66 extern int purgecmd(UAContext *ua, const char *cmd);
67 extern int querycmd(UAContext *ua, const char *cmd);
68 extern int relabel_cmd(UAContext *ua, const char *cmd);
69 extern int restore_cmd(UAContext *ua, const char *cmd);
70 extern int retentioncmd(UAContext *ua, const char *cmd);
71 extern int show_cmd(UAContext *ua, const char *cmd);
72 extern int sqlquerycmd(UAContext *ua, const char *cmd);
73 extern int status_cmd(UAContext *ua, const char *cmd);
74 extern int update_cmd(UAContext *ua, const char *cmd);
75
76 /* Forward referenced functions */
77 static int add_cmd(UAContext *ua, const char *cmd);
78 static int automount_cmd(UAContext *ua, const char *cmd);
79 static int cancel_cmd(UAContext *ua, const char *cmd);
80 static int create_cmd(UAContext *ua, const char *cmd);
81 static int delete_cmd(UAContext *ua, const char *cmd);
82 static int disable_cmd(UAContext *ua, const char *cmd);
83 static int enable_cmd(UAContext *ua, const char *cmd);
84 static int estimate_cmd(UAContext *ua, const char *cmd);
85 static int help_cmd(UAContext *ua, const char *cmd);
86 static int memory_cmd(UAContext *ua, const char *cmd);
87 static int mount_cmd(UAContext *ua, const char *cmd);
88 static int python_cmd(UAContext *ua, const char *cmd);
89 static int release_cmd(UAContext *ua, const char *cmd);
90 static int reload_cmd(UAContext *ua, const char *cmd);
91 static int setdebug_cmd(UAContext *ua, const char *cmd);
92 static int setip_cmd(UAContext *ua, const char *cmd);
93 static int time_cmd(UAContext *ua, const char *cmd);
94 static int trace_cmd(UAContext *ua, const char *cmd);
95 static int unmount_cmd(UAContext *ua, const char *cmd);
96 static int use_cmd(UAContext *ua, const char *cmd);
97 static int var_cmd(UAContext *ua, const char *cmd);
98 static int version_cmd(UAContext *ua, const char *cmd);
99 static int wait_cmd(UAContext *ua, const char *cmd);
100
101 static void do_job_delete(UAContext *ua, JobId_t JobId);
102 static bool delete_job_id_range(UAContext *ua, char *tok);
103 static int delete_volume(UAContext *ua);
104 static int delete_pool(UAContext *ua);
105 static void delete_job(UAContext *ua);
106
107 int qhelp_cmd(UAContext *ua, const char *cmd);
108 int quit_cmd(UAContext *ua, const char *cmd);
109
110 /* not all in alphabetical order.  New commands are added after existing commands with similar letters
111    to prevent breakage of existing user scripts.  */
112 struct cmdstruct { 
113    const char *key;                             /* command */
114    int (*func)(UAContext *ua, const char *cmd); /* handler */
115    const char *help;            /* main purpose */
116    const char *usage;           /* all arguments to build usage */
117    const bool use_in_rs;        /* Can use it in Console RunScript */
118 };
119 static struct cmdstruct commands[] = {                                      /* Can use it in Console RunScript*/
120  { NT_("add"),        add_cmd,     _("Add media to a pool"),   NT_("pool=<pool-name> storage=<storage> jobid=<JobId>"),  false},
121  { NT_("autodisplay"), autodisplay_cmd,_("Autodisplay console messages"), NT_("on | off"),    false},
122  { NT_("automount"),   automount_cmd,  _("Automount after label"),        NT_("on | off"),    false},
123  { NT_("cancel"),     cancel_cmd,    _("Cancel a job"), NT_("jobid=<number> job=<job-name> ujobid=<unique-jobid>"), false},
124  { NT_("create"),     create_cmd,    _("Create DB Pool from resource"), NT_("pool=<pool-name>"),                    false},
125  { NT_("delete"),     delete_cmd,    _("Delete volume, pool or job"), NT_("volume=<vol-name> pool=<pool-name> jobid=<id>"), true},
126  { NT_("disable"),    disable_cmd,   _("Disable a job"), NT_("job=<name>"),        true},
127  { NT_("enable"),     enable_cmd,    _("Enable a job"), NT_("job=<name>"),          true},
128  { NT_("estimate"),   estimate_cmd,  _("Performs FileSet estimate, listing gives full listing"), 
129    NT_("fileset=<fs> client=<cli> level=<level> accurate=<yes/no> job=<job> listing"), true},
130
131  { NT_("exit"),       quit_cmd,      _("Terminate Bconsole session"), NT_(""),         false},
132  { NT_("gui"),        gui_cmd,       _("Non-interactive gui mode"),   NT_("on | off"), false},
133  { NT_("help"),       help_cmd,      _("Print help on specific command"),  
134    NT_("add autodisplay automount cancel create delete disable\n\tenable estimate exit gui label list llist"
135        "\n\tmessages memory mount prune purge python quit query\n\trestore relabel release reload run status"
136        "\n\tsetdebug setip show sqlquery time trace unmount\n\tumount update use var version wait"),         false},
137
138  { NT_("label"),      label_cmd,     _("Label a tape"), NT_("storage=<storage> volume=<vol> pool=<pool> slot=<slot> barcodes"), false},
139  { NT_("list"),       list_cmd,      _("List objects from catalog"), 
140    NT_("pools | jobs | jobtotals | volume | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn>"), true},
141
142  { NT_("llist"),      llist_cmd,     _("Full or long list like list command"),
143    NT_("pools | jobs | jobtotals | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn>"), true},
144
145  { NT_("messages"),   messagescmd,   _("Display pending messages"),   NT_(""),    false},
146  { NT_("memory"),     memory_cmd,    _("Print current memory usage"), NT_(""),    true},
147  { NT_("mount"),      mount_cmd,     _("Mount storage"), 
148    NT_("storage=<storage-name> slot=<num> drive=<num> [ jobid=<id> | job=<job-name> ]"), false},
149
150  { NT_("prune"),      prunecmd,      _("Prune expired records from catalog"), 
151    NT_("files | jobs | pool=<pool> | client=<client-name> | volume=<volume-name> "), true},
152
153  { NT_("purge"),      purgecmd,      _("Purge records from catalog"), NT_("files jobs volume=<vol> [action=<action> devicetype=<type> pool=<pool> allpools storage=<st> drive=<num>]"),  true},
154  { NT_("python"),     python_cmd,    _("Python control commands"),    NT_(""),              false},
155  { NT_("quit"),       quit_cmd,      _("Terminate Bconsole session"), NT_(""),              false},
156  { NT_("query"),      querycmd,      _("Query catalog"),              NT_(""),              false},
157  { NT_("restore"),    restore_cmd,   _("Restore files"), 
158    NT_("where=</path> client=<client> storage=<storage> bootstrap=<file> "
159        "restorejob=<job>"
160        "\n\tcomment=<text> jobid=<jobid> done select all"), false},
161
162  { NT_("relabel"),    relabel_cmd,   _("Relabel a tape"), 
163    NT_("storage=<storage-name> oldvolume=<old-volume-name>\n\tvolume=<newvolume-name> pool=<pool>"), false},
164
165  { NT_("release"),    release_cmd,   _("Release storage"),  NT_("storage=<storage-name>"),      false},
166  { NT_("reload"),     reload_cmd,    _("Reload conf file"), NT_(""),                  true},
167  { NT_("run"),        run_cmd,       _("Run a job"), 
168    NT_("job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
169        "where=<directory-prefix>\n\twhen=<universal-time-specification>\n\tcomment=<text> yes"), false}, 
170
171  { NT_("status"),     status_cmd,    _("Report status"), 
172    NT_("all | dir=<dir-name> | director | client=<client-name> | storage=<storage-name> slots | days=nnn"), true},
173
174  { NT_("setdebug"),   setdebug_cmd,  _("Sets debug level"), 
175    NT_("level=<nn> trace=0/1 client=<client-name> | dir | storage=<storage-name> | all"), true},
176
177  { NT_("setip"),      setip_cmd,     _("Sets new client address -- if authorized"), NT_(""),   false},
178  { NT_("show"),       show_cmd,      _("Show resource records"), 
179    NT_("job=<xxx> |  pool=<yyy> | fileset=<aaa> schedule=<sss> | client=<zzz> | disabled | all"), true},
180
181  { NT_("sqlquery"),   sqlquerycmd,   _("Use SQL to query catalog"), NT_(""),          false},
182  { NT_("time"),       time_cmd,      _("Print current time"),       NT_(""),          true},
183  { NT_("trace"),      trace_cmd,     _("Turn on/off trace to file"), NT_("on | off"), true},
184  { NT_("unmount"),    unmount_cmd,   _("Unmount storage"), 
185    NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
186
187  { NT_("umount"),     unmount_cmd,   _("Umount - for old-time Unix guys, see unmount"), 
188    NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
189
190  { NT_("update"),     update_cmd,    _("Update volume, pool or stats"), 
191    NT_("stats\n\tpool=<poolname>\n\tslots storage=<storage> scan"
192        "\n\tvolume=<volname> volstatus=<status> volretention=<time-def>"
193        "\n\t pool=<pool> recycle=<yes/no> slot=<number>\n\t inchanger=<yes/no>"
194        "\n\t maxvolbytes=<size> maxvolfiles=<nb> maxvoljobs=<nb>"
195        "\n\t enable=<yes/no> recyclepool=<pool> actiononpurge=<action>"),true},
196  { NT_("use"),        use_cmd,       _("Use catalog xxx"), NT_("catalog=<catalog>"),     false},
197  { NT_("var"),        var_cmd,       _("Does variable expansion"), NT_(""),  false},
198  { NT_("version"),    version_cmd,   _("Print Director version"),  NT_(""),  true},
199  { NT_("wait"),       wait_cmd,      _("Wait until no jobs are running"), 
200    NT_("jobname=<name> | jobid=<nnn> | ujobid=<complete_name>"), false}
201 };
202
203 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
204
205 /*
206  * Execute a command from the UA
207  */
208 bool do_a_command(UAContext *ua)
209 {
210    int i;
211    int len;
212    bool ok = false;
213    bool found = false;
214    BSOCK *user = ua->UA_sock;
215
216
217    Dmsg1(900, "Command: %s\n", ua->argk[0]);
218    if (ua->argc == 0) {
219       return false;
220    }
221
222    while (ua->jcr->wstorage->size()) {
223       ua->jcr->wstorage->remove(0);
224    }
225
226    len = strlen(ua->argk[0]);
227    for (i=0; i<comsize; i++) {     /* search for command */
228       if (strncasecmp(ua->argk[0],  commands[i].key, len) == 0) {
229          /* Check if command permitted, but "quit" is always OK */
230          if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
231              !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
232             break;
233          }
234          /* Check if this command is authorized in RunScript */
235          if (ua->runscript && !commands[i].use_in_rs) {
236             ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
237             break;
238          }
239          if (ua->api) user->signal(BNET_CMD_BEGIN);
240          ok = (*commands[i].func)(ua, ua->cmd);   /* go execute command */
241          if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
242          found = true;
243          break;
244       }
245    }
246    if (!found) {
247       ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
248       ok = false;
249    }
250    return ok;
251 }
252
253 /*
254  * This is a common routine used to stuff the Pool DB record defaults
255  *   into the Media DB record just before creating a media (Volume)
256  *   record.
257  */
258 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
259 {
260    mr->PoolId = pr->PoolId;
261    bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
262    mr->Recycle = pr->Recycle;
263    mr->VolRetention = pr->VolRetention;
264    mr->VolUseDuration = pr->VolUseDuration;
265    mr->ActionOnPurge = pr->ActionOnPurge;
266    mr->RecyclePoolId = pr->RecyclePoolId;
267    mr->MaxVolJobs = pr->MaxVolJobs;
268    mr->MaxVolFiles = pr->MaxVolFiles;
269    mr->MaxVolBytes = pr->MaxVolBytes;
270    mr->LabelType = pr->LabelType;
271    mr->Enabled = 1;
272 }
273
274
275 /*
276  *  Add Volumes to an existing Pool
277  */
278 static int add_cmd(UAContext *ua, const char *cmd)
279 {
280    POOL_DBR pr;
281    MEDIA_DBR mr;
282    int num, i, max, startnum;
283    char name[MAX_NAME_LENGTH];
284    STORE *store;
285    int Slot = 0, InChanger = 0;
286
287    ua->send_msg(_(
288 "You probably don't want to be using this command since it\n"
289 "creates database records without labeling the Volumes.\n"
290 "You probably want to use the \"label\" command.\n\n"));
291
292    if (!open_client_db(ua)) {
293       return 1;
294    }
295
296    memset(&pr, 0, sizeof(pr));
297
298    if (!get_pool_dbr(ua, &pr)) {
299       return 1;
300    }
301
302    Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
303       pr.MaxVols, pr.PoolType);
304
305    while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
306       ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
307       if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
308          return 1;
309       }
310       pr.MaxVols = ua->pint32_val;
311    }
312
313    /* Get media type */
314    if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
315       bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
316    } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
317       return 1;
318    }
319
320    if (pr.MaxVols == 0) {
321       max = 1000;
322    } else {
323       max = pr.MaxVols - pr.NumVols;
324    }
325    for (;;) {
326       char buf[100];
327       bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
328       if (!get_pint(ua, buf)) {
329          return 1;
330       }
331       num = ua->pint32_val;
332       if (num < 0 || num > max) {
333          ua->warning_msg(_("The number must be between 0 and %d\n"), max);
334          continue;
335       }
336       break;
337    }
338
339    for (;;) {
340       if (num == 0) {
341          if (!get_cmd(ua, _("Enter Volume name: "))) {
342             return 1;
343          }
344       } else {
345          if (!get_cmd(ua, _("Enter base volume name: "))) {
346             return 1;
347          }
348       }
349       /* Don't allow | in Volume name because it is the volume separator character */
350       if (!is_volume_name_legal(ua, ua->cmd)) {
351          continue;
352       }
353       if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
354          ua->warning_msg(_("Volume name too long.\n"));
355          continue;
356       }
357       if (strlen(ua->cmd) == 0) {
358          ua->warning_msg(_("Volume name must be at least one character long.\n"));
359          continue;
360       }
361       break;
362    }
363
364    bstrncpy(name, ua->cmd, sizeof(name));
365    if (num > 0) {
366       bstrncat(name, "%04d", sizeof(name));
367
368       for (;;) {
369          if (!get_pint(ua, _("Enter the starting number: "))) {
370             return 1;
371          }
372          startnum = ua->pint32_val;
373          if (startnum < 1) {
374             ua->warning_msg(_("Start number must be greater than zero.\n"));
375             continue;
376          }
377          break;
378       }
379    } else {
380       startnum = 1;
381       num = 1;
382    }
383
384    if (store && store->autochanger) {
385       if (!get_pint(ua, _("Enter slot (0 for none): "))) {
386          return 1;
387       }
388       Slot = ua->pint32_val;
389       if (!get_yesno(ua, _("InChanger? yes/no: "))) {
390          return 1;
391       }
392       InChanger = ua->pint32_val;
393    }
394
395    set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
396    for (i=startnum; i < num+startnum; i++) {
397       bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
398       mr.Slot = Slot++;
399       mr.InChanger = InChanger;
400       mr.Enabled = 1;
401       set_storageid_in_mr(store, &mr);
402       Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
403       if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
404          ua->error_msg("%s", db_strerror(ua->db));
405          return 1;
406       }
407    }
408    pr.NumVols += num;
409    Dmsg0(200, "Update pool record.\n");
410    if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
411       ua->warning_msg("%s", db_strerror(ua->db));
412       return 1;
413    }
414    ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
415
416    return 1;
417 }
418
419 /*
420  * Turn auto mount on/off
421  *
422  *  automount on
423  *  automount off
424  */
425 int automount_cmd(UAContext *ua, const char *cmd)
426 {
427    char *onoff;
428
429    if (ua->argc != 2) {
430       if (!get_cmd(ua, _("Turn on or off? "))) {
431             return 1;
432       }
433       onoff = ua->cmd;
434    } else {
435       onoff = ua->argk[1];
436    }
437
438    ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
439    return 1;
440 }
441
442
443 /*
444  * Cancel a job
445  */
446 static int cancel_cmd(UAContext *ua, const char *cmd)
447 {
448    int i, ret;
449    int njobs = 0;
450    JCR *jcr = NULL;
451    char JobName[MAX_NAME_LENGTH];
452
453    for (i=1; i<ua->argc; i++) {
454       if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
455          uint32_t JobId;
456          JobId = str_to_int64(ua->argv[i]);
457          if (!JobId) {
458             break;
459          }
460          if (!(jcr=get_jcr_by_id(JobId))) {
461             ua->error_msg(_("JobId %s is not running. Use Job name to cancel inactive jobs.\n"),  ua->argv[i]);
462             return 1;
463          }
464          break;
465       } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
466          if (!ua->argv[i]) {
467             break;
468          }
469          if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
470             ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
471             jcr = new_jcr(sizeof(JCR), dird_free_jcr);
472             bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
473          }
474          break;
475       } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
476          if (!ua->argv[i]) {
477             break;
478          }
479          if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
480             ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
481             jcr = new_jcr(sizeof(JCR), dird_free_jcr);
482             bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
483          }
484          break;
485       }
486
487    }
488    if (jcr) {
489       if (jcr->job && !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
490          ua->error_msg(_("Unauthorized command from this console.\n"));
491          return 1;
492       }
493    } else {
494      /*
495       * If we still do not have a jcr,
496       *   throw up a list and ask the user to select one.
497       */
498       char buf[1000];
499       int tjobs = 0;                  /* total # number jobs */
500       /* Count Jobs running */
501       foreach_jcr(jcr) {
502          if (jcr->JobId == 0) {      /* this is us */
503             continue;
504          }
505          tjobs++;                    /* count of all jobs */
506          if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
507             continue;               /* skip not authorized */
508          }
509          njobs++;                   /* count of authorized jobs */
510       }
511       endeach_jcr(jcr);
512
513       if (njobs == 0) {            /* no authorized */
514          if (tjobs == 0) {
515             ua->send_msg(_("No Jobs running.\n"));
516          } else {
517             ua->send_msg(_("None of your jobs are running.\n"));
518          }
519          return 1;
520       }
521
522       start_prompt(ua, _("Select Job:\n"));
523       foreach_jcr(jcr) {
524          char ed1[50];
525          if (jcr->JobId == 0) {      /* this is us */
526             continue;
527          }
528          if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
529             continue;               /* skip not authorized */
530          }
531          bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
532          add_prompt(ua, buf);
533       }
534       endeach_jcr(jcr);
535
536       if (do_prompt(ua, _("Job"),  _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
537          return 1;
538       }
539       if (ua->api && njobs == 1) {
540          char nbuf[1000];
541          bsnprintf(nbuf, sizeof(nbuf), _("Cancel: %s\n\n%s"), buf,  
542                    _("Confirm cancel?"));
543          if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
544             return 1;
545          }
546       } else {
547          if (njobs == 1) {
548             if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
549                return 1;
550             }
551          }
552       }
553       sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
554       jcr = get_jcr_by_full_name(JobName);
555       if (!jcr) {
556          ua->warning_msg(_("Job \"%s\" not found.\n"), JobName);
557          return 1;
558       }
559    }
560
561    ret = cancel_job(ua, jcr);
562    free_jcr(jcr);
563    return ret;
564 }
565
566 /*
567  * This is a common routine to create or update a
568  *   Pool DB base record from a Pool Resource. We handle
569  *   the setting of MaxVols and NumVols slightly differently
570  *   depending on if we are creating the Pool or we are
571  *   simply bringing it into agreement with the resource (updage).
572  *
573  * Caution : RecyclePoolId isn't setup in this function.
574  *           You can use set_pooldbr_recyclepoolid();
575  *
576  */
577 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
578 {
579    bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
580    if (op == POOL_OP_CREATE) {
581       pr->MaxVols = pool->max_volumes;
582       pr->NumVols = 0;
583    } else {          /* update pool */
584       if (pr->MaxVols != pool->max_volumes) {
585          pr->MaxVols = pool->max_volumes;
586       }
587       if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
588          pr->MaxVols = pr->NumVols;
589       }
590    }
591    pr->LabelType = pool->LabelType;
592    pr->UseOnce = pool->use_volume_once;
593    pr->UseCatalog = pool->use_catalog;
594    pr->Recycle = pool->Recycle;
595    pr->VolRetention = pool->VolRetention;
596    pr->VolUseDuration = pool->VolUseDuration;
597    pr->MaxVolJobs = pool->MaxVolJobs;
598    pr->MaxVolFiles = pool->MaxVolFiles;
599    pr->MaxVolBytes = pool->MaxVolBytes;
600    pr->AutoPrune = pool->AutoPrune;
601    pr->ActionOnPurge = pool->action_on_purge;
602    pr->Recycle = pool->Recycle;
603    if (pool->label_format) {
604       bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
605    } else {
606       bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat));    /* none */
607    }
608 }
609
610 /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
611 int update_pool_references(JCR *jcr, B_DB *db, POOL *pool)
612 {
613    POOL_DBR  pr;
614
615    if (!pool->RecyclePool && !pool->ScratchPool) {
616       return 1;
617    }
618
619    memset(&pr, 0, sizeof(POOL_DBR));
620    bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
621
622    if (!db_get_pool_record(jcr, db, &pr)) {
623       return -1;                       /* not exists in database */
624    }
625
626    set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
627
628    if (!set_pooldbr_references(jcr, db, &pr, pool)) {
629       return -1;                      /* error */
630    }
631
632    if (!db_update_pool_record(jcr, db, &pr)) {
633       return -1;                      /* error */
634    }
635    return 1;
636 }
637
638 /* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource 
639  * works with set_pooldbr_from_poolres
640  */
641 bool set_pooldbr_references(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
642 {
643    POOL_DBR rpool;
644    bool ret = true;
645
646    if (pool->RecyclePool) {
647       memset(&rpool, 0, sizeof(POOL_DBR));
648
649       bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
650       if (db_get_pool_record(jcr, db, &rpool)) {
651         pr->RecyclePoolId = rpool.PoolId;
652       } else {
653         Jmsg(jcr, M_WARNING, 0,
654         _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
655           "Try to update it with 'update pool=%s'\n"),
656         pool->name(), rpool.Name, rpool.Name,pool->name());
657
658         ret = false;
659       }
660    } else {                    /* no RecyclePool used, set it to 0 */
661       pr->RecyclePoolId = 0;
662    }
663
664    if (pool->ScratchPool) {
665       memset(&rpool, 0, sizeof(POOL_DBR));
666
667       bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
668       if (db_get_pool_record(jcr, db, &rpool)) {
669         pr->ScratchPoolId = rpool.PoolId;
670       } else {
671         Jmsg(jcr, M_WARNING, 0,
672         _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
673           "Try to update it with 'update pool=%s'\n"),
674         pool->name(), rpool.Name, rpool.Name,pool->name());
675         ret = false;
676       }
677    } else {                    /* no ScratchPool used, set it to 0 */
678       pr->ScratchPoolId = 0;
679    }
680  
681    return ret;
682 }
683
684
685 /*
686  * Create a pool record from a given Pool resource
687  *   Also called from backup.c
688  * Returns: -1  on error
689  *           0  record already exists
690  *           1  record created
691  */
692
693 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
694 {
695    POOL_DBR  pr;
696
697    memset(&pr, 0, sizeof(POOL_DBR));
698
699    bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
700
701    if (db_get_pool_record(jcr, db, &pr)) {
702       /* Pool Exists */
703       if (op == POOL_OP_UPDATE) {  /* update request */
704          set_pooldbr_from_poolres(&pr, pool, op);
705          set_pooldbr_references(jcr, db, &pr, pool);
706          db_update_pool_record(jcr, db, &pr);
707       }
708       return 0;                       /* exists */
709    }
710
711    set_pooldbr_from_poolres(&pr, pool, op);
712    set_pooldbr_references(jcr, db, &pr, pool);
713
714    if (!db_create_pool_record(jcr, db, &pr)) {
715       return -1;                      /* error */
716    }
717    return 1;
718 }
719
720
721
722 /*
723  * Create a Pool Record in the database.
724  *  It is always created from the Resource record.
725  */
726 static int create_cmd(UAContext *ua, const char *cmd)
727 {
728    POOL *pool;
729
730    if (!open_client_db(ua)) {
731       return 1;
732    }
733
734    pool = get_pool_resource(ua);
735    if (!pool) {
736       return 1;
737    }
738
739    switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
740    case 0:
741       ua->error_msg(_("Error: Pool %s already exists.\n"
742                "Use update to change it.\n"), pool->name());
743       break;
744
745    case -1:
746       ua->error_msg("%s", db_strerror(ua->db));
747       break;
748
749    default:
750      break;
751    }
752    ua->send_msg(_("Pool %s created.\n"), pool->name());
753    return 1;
754 }
755
756
757 extern DIRRES *director;
758 extern char *configfile;
759
760 /*
761  * Python control command
762  *  python restart (restarts interpreter)
763  */
764 static int python_cmd(UAContext *ua, const char *cmd)
765 {
766 #ifdef HAVE_PYTHON
767    init_python_interpreter_args python_args;
768
769    if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
770       term_python_interpreter();
771
772       python_args.progname = director->name();
773       python_args.scriptdir = director->scripts_directory;
774       python_args.modulename = "DirStartUp";
775       python_args.configfile = configfile;
776       python_args.workingdir = director->working_directory;
777       python_args.job_getattr = job_getattr;
778       python_args.job_setattr = job_setattr;
779
780       init_python_interpreter(&python_args);
781
782       ua->send_msg(_("Python interpreter restarted.\n"));
783    } else {
784 #endif /* HAVE_PYTHON */
785       ua->warning_msg(_("Nothing done.\n"));
786 #ifdef HAVE_PYTHON
787    }
788 #endif /* HAVE_PYTHON */
789    return 1;
790 }
791
792 /*
793  * Set a new address in a Client resource. We do this only
794  *  if the Console name is the same as the Client name
795  *  and the Console can access the client.
796  */
797 static int setip_cmd(UAContext *ua, const char *cmd)
798 {
799    CLIENT *client;
800    char buf[1024];
801    if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
802       ua->error_msg(_("Unauthorized command from this console.\n"));
803       return 1;
804    }
805    LockRes();
806    client = GetClientResWithName(ua->cons->name());
807
808    if (!client) {
809       ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
810       goto get_out;
811    }
812    if (client->address) {
813       free(client->address);
814    }
815    /* MA Bug 6 remove ifdef */
816    sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
817    client->address = bstrdup(buf);
818    ua->send_msg(_("Client \"%s\" address set to %s\n"),
819             client->name(), client->address);
820 get_out:
821    UnlockRes();
822    return 1;
823 }
824
825
826 static void do_en_disable_cmd(UAContext *ua, bool setting)
827 {
828    JOB *job;
829    int i;
830
831    i = find_arg_with_value(ua, NT_("job")); 
832    if (i < 0) { 
833       job = select_enable_disable_job_resource(ua, setting);
834       if (!job) {
835          return;
836       }
837    } else {
838       LockRes();
839       job = GetJobResWithName(ua->argv[i]);
840       UnlockRes();
841    } 
842    if (!job) {
843       ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
844       return;
845    }
846
847    if (!acl_access_ok(ua, Job_ACL, job->name())) {
848       ua->error_msg(_("Unauthorized command from this console.\n"));
849       return;
850    }
851    job->enabled = setting;
852    ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
853    return;
854 }
855
856 static int enable_cmd(UAContext *ua, const char *cmd)
857 {
858    do_en_disable_cmd(ua, true);
859    return 1;
860 }
861
862 static int disable_cmd(UAContext *ua, const char *cmd)
863 {
864    do_en_disable_cmd(ua, false);
865    return 1;
866 }
867
868 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
869 {
870    BSOCK *sd;
871    JCR *jcr = ua->jcr;
872    USTORE lstore;
873    
874    lstore.store = store;
875    pm_strcpy(lstore.store_source, _("unknown source"));
876    set_wstorage(jcr, &lstore);
877    /* Try connecting for up to 15 seconds */
878    ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
879       store->name(), store->address, store->SDport);
880    if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
881       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
882       return;
883    }
884    Dmsg0(120, _("Connected to storage daemon\n"));
885    sd = jcr->store_bsock;
886    sd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
887    if (sd->recv() >= 0) {
888       ua->send_msg("%s", sd->msg);
889    }
890    sd->signal(BNET_TERMINATE);
891    sd->close();
892    jcr->store_bsock = NULL;
893    return;
894 }
895
896 /*
897  * For the client, we have the following values that can be set
898  *  level = debug level
899  *  trace = send debug output to a file
900  *  hangup = how many records to send to SD before hanging up
901  *    obviously this is most useful for testing restarting
902  *    failed jobs.
903  */
904 static void do_client_setdebug(UAContext *ua, CLIENT *client, 
905               int level, int trace, int hangup)
906 {
907    BSOCK *fd;
908
909    /* Connect to File daemon */
910
911    ua->jcr->client = client;
912    /* Try to connect for 15 seconds */
913    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
914       client->name(), client->address, client->FDport);
915    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
916       ua->error_msg(_("Failed to connect to Client.\n"));
917       return;
918    }
919    Dmsg0(120, "Connected to file daemon\n");
920    fd = ua->jcr->file_bsock;
921    fd->fsend("setdebug=%d trace=%d hangup=%d\n", level, trace, hangup);
922    if (fd->recv() >= 0) {
923       ua->send_msg("%s", fd->msg);
924    }
925    fd->signal(BNET_TERMINATE);
926    fd->close();
927    ua->jcr->file_bsock = NULL;
928    return;
929 }
930
931
932 static void do_all_setdebug(UAContext *ua, int level, int trace_flag, int hangup)
933 {
934    STORE *store, **unique_store;
935    CLIENT *client, **unique_client;
936    int i, j, found;
937
938    /* Director */
939    debug_level = level;
940
941    /* Count Storage items */
942    LockRes();
943    store = NULL;
944    i = 0;
945    foreach_res(store, R_STORAGE) {
946       i++;
947    }
948    unique_store = (STORE **) malloc(i * sizeof(STORE));
949    /* Find Unique Storage address/port */
950    store = (STORE *)GetNextRes(R_STORAGE, NULL);
951    i = 0;
952    unique_store[i++] = store;
953    while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
954       found = 0;
955       for (j=0; j<i; j++) {
956          if (strcmp(unique_store[j]->address, store->address) == 0 &&
957              unique_store[j]->SDport == store->SDport) {
958             found = 1;
959             break;
960          }
961       }
962       if (!found) {
963          unique_store[i++] = store;
964          Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
965       }
966    }
967    UnlockRes();
968
969    /* Call each unique Storage daemon */
970    for (j=0; j<i; j++) {
971       do_storage_setdebug(ua, unique_store[j], level, trace_flag);
972    }
973    free(unique_store);
974
975    /* Count Client items */
976    LockRes();
977    client = NULL;
978    i = 0;
979    foreach_res(client, R_CLIENT) {
980       i++;
981    }
982    unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
983    /* Find Unique Client address/port */
984    client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
985    i = 0;
986    unique_client[i++] = client;
987    while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
988       found = 0;
989       for (j=0; j<i; j++) {
990          if (strcmp(unique_client[j]->address, client->address) == 0 &&
991              unique_client[j]->FDport == client->FDport) {
992             found = 1;
993             break;
994          }
995       }
996       if (!found) {
997          unique_client[i++] = client;
998          Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
999       }
1000    }
1001    UnlockRes();
1002
1003    /* Call each unique File daemon */
1004    for (j=0; j<i; j++) {
1005       do_client_setdebug(ua, unique_client[j], level, trace_flag, hangup);
1006    }
1007    free(unique_client);
1008 }
1009
1010 /*
1011  * setdebug level=nn all trace=1/0
1012  */
1013 static int setdebug_cmd(UAContext *ua, const char *cmd)
1014 {
1015    STORE *store;
1016    CLIENT *client;
1017    int level;
1018    int trace_flag = -1;
1019    int hangup = -1;
1020    int i;
1021
1022    Dmsg1(120, "setdebug:%s:\n", cmd);
1023
1024    level = -1;
1025    i = find_arg_with_value(ua, "level");
1026    if (i >= 0) {
1027       level = atoi(ua->argv[i]);
1028    }
1029    if (level < 0) {
1030       if (!get_pint(ua, _("Enter new debug level: "))) {
1031          return 1;
1032       }
1033       level = ua->pint32_val;
1034    }
1035
1036    /* Look for trace flag. -1 => not change */
1037    i = find_arg_with_value(ua, "trace");
1038    if (i >= 0) {
1039       trace_flag = atoi(ua->argv[i]);
1040       if (trace_flag > 0) {
1041          trace_flag = 1;
1042       }
1043    }
1044
1045    /* Look for hangup (debug only)flag. -1 => not change */
1046    i = find_arg_with_value(ua, "hangup");
1047    if (i >= 0) {
1048       hangup = atoi(ua->argv[i]);
1049    }
1050
1051
1052    /* General debug? */
1053    for (i=1; i<ua->argc; i++) {
1054       if (strcasecmp(ua->argk[i], "all") == 0) {
1055          do_all_setdebug(ua, level, trace_flag, hangup);
1056          return 1;
1057       }
1058       if (strcasecmp(ua->argk[i], "dir") == 0 ||
1059           strcasecmp(ua->argk[i], "director") == 0) {
1060          debug_level = level;
1061          set_trace(trace_flag);
1062          return 1;
1063       }
1064       if (strcasecmp(ua->argk[i], "client") == 0 ||
1065           strcasecmp(ua->argk[i], "fd") == 0) {
1066          client = NULL;
1067          if (ua->argv[i]) {
1068             client = GetClientResWithName(ua->argv[i]);
1069             if (client) {
1070                do_client_setdebug(ua, client, level, trace_flag, hangup);
1071                return 1;
1072             }
1073          }
1074          client = select_client_resource(ua);
1075          if (client) {
1076             do_client_setdebug(ua, client, level, trace_flag, hangup);
1077             return 1;
1078          }
1079       }
1080
1081       if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1082           strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1083           strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1084          store = NULL;
1085          if (ua->argv[i]) {
1086             store = GetStoreResWithName(ua->argv[i]);
1087             if (store) {
1088                do_storage_setdebug(ua, store, level, trace_flag);
1089                return 1;
1090             }
1091          }
1092          store = get_storage_resource(ua, false/*no default*/);
1093          if (store) {
1094             do_storage_setdebug(ua, store, level, trace_flag);
1095             return 1;
1096          }
1097       }
1098    }
1099    /*
1100     * We didn't find an appropriate keyword above, so
1101     * prompt the user.
1102     */
1103    start_prompt(ua, _("Available daemons are: \n"));
1104    add_prompt(ua, _("Director"));
1105    add_prompt(ua, _("Storage"));
1106    add_prompt(ua, _("Client"));
1107    add_prompt(ua, _("All"));
1108    switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1109    case 0:                         /* Director */
1110       debug_level = level;
1111       set_trace(trace_flag);
1112       break;
1113    case 1:
1114       store = get_storage_resource(ua, false/*no default*/);
1115       if (store) {
1116          do_storage_setdebug(ua, store, level, trace_flag);
1117       }
1118       break;
1119    case 2:
1120       client = select_client_resource(ua);
1121       if (client) {
1122          do_client_setdebug(ua, client, level, trace_flag, hangup);
1123       }
1124       break;
1125    case 3:
1126       do_all_setdebug(ua, level, trace_flag, hangup);
1127       break;
1128    default:
1129       break;
1130    }
1131    return 1;
1132 }
1133
1134 /*
1135  * Turn debug tracing to file on/off
1136  */
1137 static int trace_cmd(UAContext *ua, const char *cmd)
1138 {
1139    char *onoff;
1140
1141    if (ua->argc != 2) {
1142       if (!get_cmd(ua, _("Turn on or off? "))) {
1143             return 1;
1144       }
1145       onoff = ua->cmd;
1146    } else {
1147       onoff = ua->argk[1];
1148    }
1149
1150    set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1151    return 1;
1152
1153 }
1154
1155 static int var_cmd(UAContext *ua, const char *cmd)
1156 {
1157    POOLMEM *val = get_pool_memory(PM_FNAME);
1158    char *var;
1159
1160    if (!open_client_db(ua)) {
1161       return 1;
1162    }
1163    for (var=ua->cmd; *var != ' '; ) {    /* skip command */
1164       var++;
1165    }
1166    while (*var == ' ') {                 /* skip spaces */
1167       var++;
1168    }
1169    Dmsg1(100, "Var=%s:\n", var);
1170    variable_expansion(ua->jcr, var, &val);
1171    ua->send_msg("%s\n", val);
1172    free_pool_memory(val);
1173    return 1;
1174 }
1175
1176 static int estimate_cmd(UAContext *ua, const char *cmd)
1177 {
1178    JOB *job = NULL;
1179    CLIENT *client = NULL;
1180    FILESET *fileset = NULL;
1181    int listing = 0;
1182    char since[MAXSTRING];
1183    JCR *jcr = ua->jcr;
1184    int accurate=-1;
1185
1186    jcr->setJobLevel(L_FULL);
1187    for (int i=1; i<ua->argc; i++) {
1188       if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1189           strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1190          if (ua->argv[i]) {
1191             client = GetClientResWithName(ua->argv[i]);
1192             if (!client) {
1193                ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1194                return 1;
1195             }
1196             if (!acl_access_ok(ua, Client_ACL, client->name())) {
1197                ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1198                return 1;
1199             }
1200             continue;
1201          } else {
1202             ua->error_msg(_("Client name missing.\n"));
1203             return 1;
1204          }
1205       }
1206       if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1207          if (ua->argv[i]) {
1208             job = GetJobResWithName(ua->argv[i]);
1209             if (!job) {
1210                ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1211                return 1;
1212             }
1213             if (!acl_access_ok(ua, Job_ACL, job->name())) {
1214                ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1215                return 1;
1216             }
1217             continue;
1218          } else {
1219             ua->error_msg(_("Job name missing.\n"));
1220             return 1;
1221          }
1222
1223       }
1224       if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1225          if (ua->argv[i]) {
1226             fileset = GetFileSetResWithName(ua->argv[i]);
1227             if (!fileset) {
1228                ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1229                return 1;
1230             }
1231             if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1232                ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1233                return 1;
1234             }
1235             continue;
1236          } else {
1237             ua->error_msg(_("Fileset name missing.\n"));
1238             return 1;
1239          }
1240       }
1241       if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1242          listing = 1;
1243          continue;
1244       }
1245       if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1246          if (ua->argv[i]) {
1247             if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1248                ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1249             }
1250             continue;
1251          } else {
1252             ua->error_msg(_("Level value missing.\n"));
1253             return 1;
1254          }
1255       }
1256       if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1257          if (ua->argv[i]) {
1258             if (!is_yesno(ua->argv[i], &accurate)) {
1259                ua->error_msg(_("Invalid value for accurate. " 
1260                                "It must be yes or no.\n"));
1261             }
1262             continue;
1263          } else {
1264             ua->error_msg(_("Accurate value missing.\n"));
1265             return 1;
1266          }
1267       }
1268    }
1269    if (!job && !(client && fileset)) {
1270       if (!(job = select_job_resource(ua))) {
1271          return 1;
1272       }
1273    }
1274    if (!job) {
1275       job = GetJobResWithName(ua->argk[1]);
1276       if (!job) {
1277          ua->error_msg(_("No job specified.\n"));
1278          return 1;
1279       }
1280       if (!acl_access_ok(ua, Job_ACL, job->name())) {
1281          ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1282          return 1;
1283       }
1284    }
1285    if (!client) {
1286       client = job->client;
1287    }
1288    if (!fileset) {
1289       fileset = job->fileset;
1290    }
1291    jcr->client = client;
1292    jcr->fileset = fileset;
1293    close_db(ua);
1294    if (job->pool->catalog) {
1295       ua->catalog = job->pool->catalog;
1296    } else {
1297       ua->catalog = client->catalog;
1298    }
1299
1300    if (!open_db(ua)) {
1301       return 1;
1302    }
1303
1304    jcr->job = job;
1305    jcr->setJobType(JT_BACKUP);
1306    jcr->start_time = time(NULL);
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    /* The level string change if accurate mode is enabled */
1326    if (accurate >= 0) {
1327       jcr->accurate = accurate;
1328    } else {
1329       jcr->accurate = job->accurate;
1330    }
1331
1332    if (!send_level_command(jcr)) {
1333       goto bail_out;
1334    }
1335
1336    if (!send_include_list(jcr)) {
1337       ua->error_msg(_("Error sending include list.\n"));
1338       goto bail_out;
1339    }
1340
1341    if (!send_exclude_list(jcr)) {
1342       ua->error_msg(_("Error sending exclude list.\n"));
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 }