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