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