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