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