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