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