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