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