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