]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_cmds.c
ebl Add estimate accurate=yes/no
[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    int accurate=-1;
1141
1142    jcr->set_JobLevel(L_FULL);
1143    for (int i=1; i<ua->argc; i++) {
1144       if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1145           strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1146          if (ua->argv[i]) {
1147             client = GetClientResWithName(ua->argv[i]);
1148             if (!client) {
1149                ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1150                return 1;
1151             }
1152             continue;
1153          } else {
1154             ua->error_msg(_("Client name missing.\n"));
1155             return 1;
1156          }
1157       }
1158       if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1159          if (ua->argv[i]) {
1160             job = GetJobResWithName(ua->argv[i]);
1161             if (!job) {
1162                ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1163                return 1;
1164             }
1165             if (!acl_access_ok(ua, Job_ACL, job->name())) {
1166                ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1167                return 1;
1168             }
1169             continue;
1170          } else {
1171             ua->error_msg(_("Job name missing.\n"));
1172             return 1;
1173          }
1174
1175       }
1176       if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1177          if (ua->argv[i]) {
1178             fileset = GetFileSetResWithName(ua->argv[i]);
1179             if (!fileset) {
1180                ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1181                return 1;
1182             }
1183             if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1184                ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1185                return 1;
1186             }
1187             continue;
1188          } else {
1189             ua->error_msg(_("Fileset name missing.\n"));
1190             return 1;
1191          }
1192       }
1193       if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1194          listing = 1;
1195          continue;
1196       }
1197       if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1198          if (ua->argv[i]) {
1199             if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1200                ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1201             }
1202             continue;
1203          } else {
1204            ua->error_msg(_("Level value missing.\n"));
1205            return 1;
1206          }
1207       }
1208       if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1209          if (!is_yesno(ua->argv[i], &accurate)) {
1210             ua->error_msg(_("Invalid value for accurate. " 
1211                             "It must be yes or no.\n"));
1212          }
1213       }
1214    }
1215    if (!job && !(client && fileset)) {
1216       if (!(job = select_job_resource(ua))) {
1217          return 1;
1218       }
1219    }
1220    if (!job) {
1221       job = GetJobResWithName(ua->argk[1]);
1222       if (!job) {
1223          ua->error_msg(_("No job specified.\n"));
1224          return 1;
1225       }
1226       if (!acl_access_ok(ua, Job_ACL, job->name())) {
1227          ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1228          return 1;
1229       }
1230    }
1231    if (!client) {
1232       client = job->client;
1233    }
1234    if (!fileset) {
1235       fileset = job->fileset;
1236    }
1237    jcr->client = client;
1238    jcr->fileset = fileset;
1239    close_db(ua);
1240    if (job->pool->catalog) {
1241       ua->catalog = job->pool->catalog;
1242    } else {
1243       ua->catalog = client->catalog;
1244    }
1245
1246    if (!open_db(ua)) {
1247       return 1;
1248    }
1249
1250    jcr->job = job;
1251    jcr->set_JobType(JT_BACKUP);
1252    init_jcr_job_record(jcr);
1253
1254    if (!get_or_create_client_record(jcr)) {
1255       return 1;
1256    }
1257    if (!get_or_create_fileset_record(jcr)) {
1258       return 1;
1259    }
1260
1261    get_level_since_time(ua->jcr, since, sizeof(since));
1262
1263    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1264       jcr->client->name(), jcr->client->address, jcr->client->FDport);
1265    if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1266       ua->error_msg(_("Failed to connect to Client.\n"));
1267       return 1;
1268    }
1269
1270    if (!send_include_list(jcr)) {
1271       ua->error_msg(_("Error sending include list.\n"));
1272       goto bail_out;
1273    }
1274
1275    if (!send_exclude_list(jcr)) {
1276       ua->error_msg(_("Error sending exclude list.\n"));
1277       goto bail_out;
1278    }
1279
1280    /* The level string change if accurate mode is enabled */
1281    if (accurate >= 0) {
1282       jcr->accurate = accurate;
1283    } else {
1284       jcr->accurate = job->accurate;
1285    }
1286
1287    if (!send_level_command(jcr)) {
1288       goto bail_out;
1289    }
1290
1291    /*
1292     * If the job is in accurate mode, we send the list of
1293     * all files to FD.
1294     */
1295    Dmsg1(40, "estimate accurate=%i\n", jcr->accurate);
1296    if (!send_accurate_current_files(jcr)) {
1297       goto bail_out;
1298    }
1299
1300    bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1301    while (bnet_recv(jcr->file_bsock) >= 0) {
1302       ua->send_msg("%s", jcr->file_bsock->msg);
1303    }
1304
1305 bail_out:
1306    if (jcr->file_bsock) {
1307       bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1308       bnet_close(jcr->file_bsock);
1309       jcr->file_bsock = NULL;
1310    }
1311    return 1;
1312 }
1313
1314
1315 /*
1316  * print time
1317  */
1318 static int time_cmd(UAContext *ua, const char *cmd)
1319 {
1320    char sdt[50];
1321    time_t ttime = time(NULL);
1322    struct tm tm;
1323    (void)localtime_r(&ttime, &tm);
1324    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1325    ua->send_msg("%s\n", sdt);
1326    return 1;
1327 }
1328
1329 /*
1330  * reload the conf file
1331  */
1332 extern "C" void reload_config(int sig);
1333
1334 static int reload_cmd(UAContext *ua, const char *cmd)
1335 {
1336    reload_config(1);
1337    return 1;
1338 }
1339
1340 /*
1341  * Delete Pool records (should purge Media with it).
1342  *
1343  *  delete pool=<pool-name>
1344  *  delete volume pool=<pool-name> volume=<name>
1345  *  delete jobid=xxx
1346  */
1347 static int delete_cmd(UAContext *ua, const char *cmd)
1348 {
1349    static const char *keywords[] = {
1350       NT_("volume"),
1351       NT_("pool"),
1352       NT_("jobid"),
1353       NULL};
1354
1355    if (!open_client_db(ua)) {
1356       return 1;
1357    }
1358
1359    switch (find_arg_keyword(ua, keywords)) {
1360    case 0:
1361       delete_volume(ua);
1362       return 1;
1363    case 1:
1364       delete_pool(ua);
1365       return 1;
1366    case 2:
1367       int i;
1368       while ((i=find_arg(ua, "jobid")) > 0) {
1369          delete_job(ua);
1370          *ua->argk[i] = 0;         /* zap keyword already visited */
1371       }
1372       return 1;
1373    default:
1374       break;
1375    }
1376
1377    ua->warning_msg(_(
1378 "In general it is not a good idea to delete either a\n"
1379 "Pool or a Volume since they may contain data.\n\n"));
1380
1381    switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1382    case 0:
1383       delete_volume(ua);
1384       break;
1385    case 1:
1386       delete_pool(ua);
1387       break;
1388    case 2:
1389       delete_job(ua);
1390       return 1;
1391    default:
1392       ua->warning_msg(_("Nothing done.\n"));
1393       break;
1394    }
1395    return 1;
1396 }
1397
1398
1399 /*
1400  * delete_job has been modified to parse JobID lists like the
1401  * following:
1402  * delete JobID=3,4,6,7-11,14
1403  *
1404  * Thanks to Phil Stracchino for the above addition.
1405  */
1406
1407 static void delete_job(UAContext *ua)
1408 {
1409    JobId_t JobId;
1410    char *s,*sep,*tok;
1411
1412    int i = find_arg_with_value(ua, NT_("jobid"));
1413    if (i >= 0) {
1414       if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1415         s = bstrdup(ua->argv[i]);
1416         tok = s;
1417         /*
1418          * We could use strtok() here.  But we're not going to, because:
1419          * (a) strtok() is deprecated, having been replaced by strsep();
1420          * (b) strtok() is broken in significant ways.
1421          * we could use strsep() instead, but it's not universally available.
1422          * so we grow our own using strchr().
1423          */
1424         sep = strchr(tok, ',');
1425         while (sep != NULL) {
1426            *sep = '\0';
1427            if (strchr(tok, '-')) {
1428                delete_job_id_range(ua, tok);
1429            } else {
1430               JobId = str_to_int64(tok);
1431               do_job_delete(ua, JobId);
1432            }
1433            tok = ++sep;
1434            sep = strchr(tok, ',');
1435         }
1436         /* pick up the last token */
1437         if (strchr(tok, '-')) {
1438             delete_job_id_range(ua, tok);
1439         } else {
1440             JobId = str_to_int64(tok);
1441             do_job_delete(ua, JobId);
1442         }
1443
1444          free(s);
1445       } else {
1446          JobId = str_to_int64(ua->argv[i]);
1447         do_job_delete(ua, JobId);
1448       }
1449    } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1450       return;
1451    } else {
1452       JobId = ua->int64_val;
1453       do_job_delete(ua, JobId);
1454    }
1455 }
1456
1457 /*
1458  * we call delete_job_id_range to parse range tokens and iterate over ranges
1459  */
1460 static void delete_job_id_range(UAContext *ua, char *tok)
1461 {
1462    char *tok2;
1463    JobId_t j,j1,j2;
1464
1465    tok2 = strchr(tok, '-');
1466    *tok2 = '\0';
1467    tok2++;
1468    j1 = str_to_int64(tok);
1469    j2 = str_to_int64(tok2);
1470    for (j=j1; j<=j2; j++) {
1471       do_job_delete(ua, j);
1472    }
1473 }
1474
1475 /*
1476  * do_job_delete now performs the actual delete operation atomically
1477  */
1478 static void do_job_delete(UAContext *ua, JobId_t JobId)
1479 {
1480    char ed1[50];
1481
1482    edit_int64(JobId, ed1);
1483    purge_jobs_from_catalog(ua, ed1);
1484    ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1485 }
1486
1487 /*
1488  * Delete media records from database -- dangerous
1489  */
1490 static int delete_volume(UAContext *ua)
1491 {
1492    MEDIA_DBR mr;
1493    char buf[1000];
1494
1495    if (!select_media_dbr(ua, &mr)) {
1496       return 1;
1497    }
1498    ua->warning_msg(_("\nThis command will delete volume %s\n"
1499       "and all Jobs saved on that volume from the Catalog\n"),
1500       mr.VolumeName);
1501
1502    if (find_arg(ua, "yes") >= 0) {
1503       ua->pint32_val = 1; /* Have "yes" on command line already" */
1504    } else {
1505       bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1506          mr.VolumeName);
1507       if (!get_yesno(ua, buf)) {
1508          return 1;
1509       }
1510    }
1511    if (ua->pint32_val) {
1512       db_delete_media_record(ua->jcr, ua->db, &mr);
1513    }
1514    return 1;
1515 }
1516
1517 /*
1518  * Delete a pool record from the database -- dangerous
1519  */
1520 static int delete_pool(UAContext *ua)
1521 {
1522    POOL_DBR  pr;
1523    char buf[200];
1524
1525    memset(&pr, 0, sizeof(pr));
1526
1527    if (!get_pool_dbr(ua, &pr)) {
1528       return 1;
1529    }
1530    bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1531       pr.Name);
1532    if (!get_yesno(ua, buf)) {
1533       return 1;
1534    }
1535    if (ua->pint32_val) {
1536       db_delete_pool_record(ua->jcr, ua->db, &pr);
1537    }
1538    return 1;
1539 }
1540
1541 int memory_cmd(UAContext *ua, const char *cmd)
1542 {
1543    list_dir_status_header(ua);
1544    sm_dump(false, true);
1545    return 1;
1546 }
1547
1548 static void do_mount_cmd(UAContext *ua, const char *command)
1549 {
1550    USTORE store;
1551    BSOCK *sd;
1552    JCR *jcr = ua->jcr;
1553    char dev_name[MAX_NAME_LENGTH];
1554    int drive;
1555    int slot = -1;
1556
1557    if (!open_client_db(ua)) {
1558       return;
1559    }
1560    Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1561
1562    store.store = get_storage_resource(ua, true/*arg is storage*/);
1563    if (!store.store) {
1564       return;
1565    }
1566    pm_strcpy(store.store_source, _("unknown source"));
1567    set_wstorage(jcr, &store);
1568    drive = get_storage_drive(ua, store.store);
1569    if (strcmp(command, "mount") == 0) {
1570       slot = get_storage_slot(ua, store.store);
1571    }
1572
1573    Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1574       store.store->media_type, store.store->dev_name(), drive);
1575
1576    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1577       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1578       return;
1579    }
1580    sd = jcr->store_bsock;
1581    bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1582    bash_spaces(dev_name);
1583    if (slot > 0) {
1584       bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1585    } else {
1586       bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1587    }
1588    while (bnet_recv(sd) >= 0) {
1589       ua->send_msg("%s", sd->msg);
1590    }
1591    bnet_sig(sd, BNET_TERMINATE);
1592    bnet_close(sd);
1593    jcr->store_bsock = NULL;
1594 }
1595
1596 /*
1597  * mount [storage=<name>] [drive=nn] [slot=mm]
1598  */
1599 static int mount_cmd(UAContext *ua, const char *cmd)
1600 {
1601    do_mount_cmd(ua, "mount");          /* mount */
1602    return 1;
1603 }
1604
1605
1606 /*
1607  * unmount [storage=<name>] [drive=nn]
1608  */
1609 static int unmount_cmd(UAContext *ua, const char *cmd)
1610 {
1611    do_mount_cmd(ua, "unmount");          /* unmount */
1612    return 1;
1613 }
1614
1615
1616 /*
1617  * release [storage=<name>] [drive=nn]
1618  */
1619 static int release_cmd(UAContext *ua, const char *cmd)
1620 {
1621    do_mount_cmd(ua, "release");          /* release */
1622    return 1;
1623 }
1624
1625
1626 /*
1627  * Switch databases
1628  *   use catalog=<name>
1629  */
1630 static int use_cmd(UAContext *ua, const char *cmd)
1631 {
1632    CAT *oldcatalog, *catalog;
1633
1634
1635    close_db(ua);                      /* close any previously open db */
1636    oldcatalog = ua->catalog;
1637
1638    if (!(catalog = get_catalog_resource(ua))) {
1639       ua->catalog = oldcatalog;
1640    } else {
1641       ua->catalog = catalog;
1642    }
1643    if (open_db(ua)) {
1644       ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1645          ua->catalog->name(), ua->catalog->db_name);
1646    }
1647    return 1;
1648 }
1649
1650 int quit_cmd(UAContext *ua, const char *cmd)
1651 {
1652    ua->quit = true;
1653    return 1;
1654 }
1655
1656 /* Handler to get job status */
1657 static int status_handler(void *ctx, int num_fields, char **row)
1658 {
1659    char *val = (char *)ctx;
1660
1661    if (row[0]) {
1662       *val = row[0][0];
1663    } else {
1664       *val = '?';               /* Unknown by default */
1665    }
1666
1667    return 0;
1668 }
1669
1670 /*
1671  * Wait until no job is running
1672  */
1673 int wait_cmd(UAContext *ua, const char *cmd)
1674 {
1675    JCR *jcr;
1676    int i;
1677    time_t stop_time = 0;
1678
1679    /*
1680     * no args
1681     * Wait until no job is running
1682     */
1683    if (ua->argc == 1) {
1684       bmicrosleep(0, 200000);            /* let job actually start */
1685       for (bool running=true; running; ) {
1686          running = false;
1687          foreach_jcr(jcr) {
1688             if (jcr->JobId != 0) {
1689                running = true;
1690                break;
1691             }
1692          }
1693          endeach_jcr(jcr);
1694
1695          if (running) {
1696             bmicrosleep(1, 0);
1697          }
1698       }
1699       return 1;
1700    }
1701
1702    i = find_arg_with_value(ua, NT_("timeout")); 
1703    if (i > 0 && ua->argv[i]) {
1704       stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1705    }
1706
1707    /* we have jobid, jobname or ujobid argument */
1708
1709    uint32_t jobid = 0 ;
1710
1711    if (!open_client_db(ua)) {
1712       ua->error_msg(_("ERR: Can't open db\n")) ;
1713       return 1;
1714    }
1715
1716    for (int i=1; i<ua->argc; i++) {
1717       if (strcasecmp(ua->argk[i], "jobid") == 0) {
1718          if (!ua->argv[i]) {
1719             break;
1720          }
1721          jobid = str_to_int64(ua->argv[i]);
1722          break;
1723       } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1724                  strcasecmp(ua->argk[i], "job") == 0) {
1725          if (!ua->argv[i]) {
1726             break;
1727          }
1728          jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1729          if (jcr) {
1730             jobid = jcr->JobId ;
1731             free_jcr(jcr);
1732          }
1733          break;
1734       } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1735          if (!ua->argv[i]) {
1736             break;
1737          }
1738          jcr=get_jcr_by_full_name(ua->argv[i]) ;
1739          if (jcr) {
1740             jobid = jcr->JobId ;
1741             free_jcr(jcr);
1742          }
1743          break;
1744       /* Wait for a mount request */
1745       } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1746          for (bool waiting=false; !waiting; ) {
1747             foreach_jcr(jcr) {
1748                if (jcr->JobId != 0 && 
1749                    (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1750                   waiting = true;
1751                   break;
1752                }
1753             }
1754             endeach_jcr(jcr);
1755             if (waiting) {
1756                break;
1757             }
1758             if (stop_time && (time(NULL) >= stop_time)) {
1759                ua->warning_msg(_("Wait on mount timed out\n"));
1760                return 1;
1761             }
1762             bmicrosleep(1, 0);
1763          }
1764          return 1;
1765       }
1766    }
1767
1768    if (jobid == 0) {
1769       ua->error_msg(_("ERR: Job was not found\n"));
1770       return 1 ;
1771    }
1772
1773    /*
1774     * We wait the end of a specific job
1775     */
1776
1777    bmicrosleep(0, 200000);            /* let job actually start */
1778    for (bool running=true; running; ) {
1779       running = false;
1780
1781       jcr=get_jcr_by_id(jobid) ;
1782
1783       if (jcr) {
1784          running = true ;
1785          free_jcr(jcr);
1786       }
1787
1788       if (running) {
1789          bmicrosleep(1, 0);
1790       }
1791    }
1792
1793    /*
1794     * We have to get JobStatus
1795     */
1796
1797    int status ;
1798    char jobstatus = '?';        /* Unknown by default */
1799    char buf[256] ;
1800
1801    bsnprintf(buf, sizeof(buf),
1802              "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1803
1804
1805    db_sql_query(ua->db, buf,
1806                 status_handler, (void *)&jobstatus);
1807
1808    switch (jobstatus) {
1809    case JS_Error:
1810       status = 1 ;         /* Warning */
1811       break;
1812
1813    case JS_FatalError:
1814    case JS_ErrorTerminated:
1815    case JS_Canceled:
1816       status = 2 ;         /* Critical */
1817       break;
1818
1819    case JS_Warnings:
1820    case JS_Terminated:
1821       status = 0 ;         /* Ok */
1822       break;
1823
1824    default:
1825       status = 3 ;         /* Unknown */
1826       break;
1827    }
1828
1829    ua->send_msg("JobId=%i\n", jobid) ;
1830    ua->send_msg("JobStatus=%s (%c)\n", 
1831             job_status_to_str(jobstatus), 
1832             jobstatus) ;
1833
1834    if (ua->gui || ua->api) {
1835       ua->send_msg("ExitStatus=%i\n", status) ;
1836    }
1837
1838    return 1;
1839 }
1840
1841
1842 static int help_cmd(UAContext *ua, const char *cmd)
1843 {
1844    int i;
1845
1846    ua->send_msg(_("  Command    Description\n  =======    ===========\n"));
1847    for (i=0; i<comsize; i++) {
1848       ua->send_msg(_("  %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1849    }
1850    ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1851    return 1;
1852 }
1853
1854 int qhelp_cmd(UAContext *ua, const char *cmd)
1855 {
1856    int i;
1857
1858    for (i=0; i<comsize; i++) {
1859       ua->send_msg("%s %s\n", commands[i].key, _(commands[i].help));
1860    }
1861    return 1;
1862 }
1863
1864 #if 1 
1865 static int version_cmd(UAContext *ua, const char *cmd)
1866 {
1867    ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
1868                 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
1869    return 1;
1870 }
1871 #else
1872 /*
1873  *  Test code -- turned on only for debug testing 
1874  */
1875 static int version_cmd(UAContext *ua, const char *cmd)
1876 {
1877    dbid_list ids;
1878    POOL_MEM query(PM_MESSAGE);
1879    open_db(ua);
1880    Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1881    db_get_query_dbids(ua->jcr, ua->db, query, ids);
1882    ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1883    for (int i=0; i < ids.num_ids; i++) {
1884       ua->send_msg("id=%d\n", ids.DBId[i]);
1885    }
1886    close_db(ua);
1887    return 1;
1888 }
1889 #endif
1890
1891 /* 
1892  * This call explicitly checks for a catalog=xxx and
1893  *  if given, opens that catalog.  It also checks for
1894  *  client=xxx and if found, opens the catalog 
1895  *  corresponding to that client. If we still don't 
1896  *  have a catalog, look for a Job keyword and get the
1897  *  catalog from its client record.
1898  */
1899 bool open_client_db(UAContext *ua)
1900 {
1901    int i;
1902    CAT *catalog;
1903    CLIENT *client;
1904    JOB *job;
1905
1906    /* Try for catalog keyword */
1907    i = find_arg_with_value(ua, NT_("catalog"));
1908    if (i >= 0) {
1909       if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1910          ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1911          return false;
1912       }
1913       catalog = GetCatalogResWithName(ua->argv[i]);
1914       if (catalog) {
1915          if (ua->catalog && ua->catalog != catalog) {
1916             close_db(ua);
1917          }
1918          ua->catalog = catalog;
1919          return open_db(ua);
1920       }
1921    }
1922
1923    /* Try for client keyword */
1924    i = find_arg_with_value(ua, NT_("client"));
1925    if (i >= 0) {
1926       if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1927          ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
1928          return false;
1929       }
1930       client = GetClientResWithName(ua->argv[i]);
1931       if (client) {
1932          catalog = client->catalog;
1933          if (ua->catalog && ua->catalog != catalog) {
1934             close_db(ua);
1935          }
1936          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1937             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1938             return false;
1939          }
1940          ua->catalog = catalog;
1941          return open_db(ua);
1942       }
1943    }
1944
1945    /* Try for Job keyword */
1946    i = find_arg_with_value(ua, NT_("job"));
1947    if (i >= 0) {
1948       if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1949          ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
1950          return false;
1951       }
1952       job = GetJobResWithName(ua->argv[i]);
1953       if (job) {
1954          catalog = job->client->catalog;
1955          if (ua->catalog && ua->catalog != catalog) {
1956             close_db(ua);
1957          }
1958          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1959             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1960             return false;
1961          }
1962          ua->catalog = catalog;
1963          return open_db(ua);
1964       }
1965    }
1966
1967    return open_db(ua);
1968 }
1969
1970
1971 /*
1972  * Open the catalog database.
1973  */
1974 bool open_db(UAContext *ua)
1975 {
1976    if (ua->db) {
1977       return true;
1978    }
1979    if (!ua->catalog) {
1980       ua->catalog = get_catalog_resource(ua);
1981       if (!ua->catalog) {
1982          ua->error_msg( _("Could not find a Catalog resource\n"));
1983          return false;
1984       }
1985    }
1986
1987    ua->jcr->catalog = ua->catalog;
1988
1989    Dmsg0(100, "UA Open database\n");
1990    ua->db = db_init(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name, 
1991                              ua->catalog->db_user,
1992                              ua->catalog->db_password, ua->catalog->db_address,
1993                              ua->catalog->db_port, ua->catalog->db_socket,
1994                              ua->catalog->mult_db_connections);
1995    if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1996       ua->error_msg(_("Could not open catalog database \"%s\".\n"),
1997                  ua->catalog->db_name);
1998       if (ua->db) {
1999          ua->error_msg("%s", db_strerror(ua->db));
2000       }
2001       close_db(ua);
2002       return false;
2003    }
2004    ua->jcr->db = ua->db;
2005    if (!ua->api) {
2006       ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name()); 
2007    }
2008    Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2009    return true;
2010 }
2011
2012 void close_db(UAContext *ua)
2013 {
2014    if (ua->db) {
2015       db_close_database(ua->jcr, ua->db);
2016       ua->db = NULL;
2017       if (ua->jcr) {
2018          ua->jcr->db = NULL;
2019       }
2020    }
2021 }