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