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