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