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