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