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