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