]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_cmds.c
Fix bug #2300 mount/unmount/release of single tape drive does not work
[bacula/bacula] / bacula / src / dird / ua_cmds.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *   Bacula Director -- User Agent Commands
21  *
22  *     Kern Sibbald, September MM
23  */
24
25 #include "bacula.h"
26 #include "dird.h"
27
28 /* Imported subroutines */
29
30 /* Imported variables */
31 extern jobq_t job_queue;              /* job queue */
32
33
34 /* Imported functions */
35 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
36 extern int gui_cmd(UAContext *ua, const char *cmd);
37 extern int label_cmd(UAContext *ua, const char *cmd);
38 extern int list_cmd(UAContext *ua, const char *cmd);
39 extern int llist_cmd(UAContext *ua, const char *cmd);
40 extern int messagescmd(UAContext *ua, const char *cmd);
41 extern int prunecmd(UAContext *ua, const char *cmd);
42 extern int purge_cmd(UAContext *ua, const char *cmd);
43 extern int truncate_cmd(UAContext *ua, const char *cmd);  /* in ua_purge.c */
44 extern int query_cmd(UAContext *ua, const char *cmd);
45 extern int relabel_cmd(UAContext *ua, const char *cmd);
46 extern int restore_cmd(UAContext *ua, const char *cmd);
47 extern int retentioncmd(UAContext *ua, const char *cmd);
48 extern int show_cmd(UAContext *ua, const char *cmd);
49 extern int sqlquery_cmd(UAContext *ua, const char *cmd);
50 extern int status_cmd(UAContext *ua, const char *cmd);
51 extern int update_cmd(UAContext *ua, const char *cmd);
52
53 /* Forward referenced functions */
54 static int add_cmd(UAContext *ua, const char *cmd);
55 static int automount_cmd(UAContext *ua, const char *cmd);
56 static int cancel_cmd(UAContext *ua, const char *cmd);
57 static int create_cmd(UAContext *ua, const char *cmd);
58 static int delete_cmd(UAContext *ua, const char *cmd);
59 static int disable_cmd(UAContext *ua, const char *cmd);
60 static int enable_cmd(UAContext *ua, const char *cmd);
61 static int estimate_cmd(UAContext *ua, const char *cmd);
62 static int help_cmd(UAContext *ua, const char *cmd);
63 static int memory_cmd(UAContext *ua, const char *cmd);
64 static int mount_cmd(UAContext *ua, const char *cmd);
65 static int release_cmd(UAContext *ua, const char *cmd);
66 static int reload_cmd(UAContext *ua, const char *cmd);
67 static int setdebug_cmd(UAContext *ua, const char *cmd);
68 static int setbwlimit_cmd(UAContext *ua, const char *cmd);
69 static int setip_cmd(UAContext *ua, const char *cmd);
70 static int time_cmd(UAContext *ua, const char *cmd);
71 static int trace_cmd(UAContext *ua, const char *cmd);
72 static int unmount_cmd(UAContext *ua, const char *cmd);
73 static int use_cmd(UAContext *ua, const char *cmd);
74 static int cloud_cmd(UAContext *ua, const char *cmd);
75 static int var_cmd(UAContext *ua, const char *cmd);
76 static int version_cmd(UAContext *ua, const char *cmd);
77 static int wait_cmd(UAContext *ua, const char *cmd);
78
79 static void do_job_delete(UAContext *ua, JobId_t JobId);
80 static int delete_volume(UAContext *ua);
81 static int delete_pool(UAContext *ua);
82 static void delete_job(UAContext *ua);
83 static void do_storage_cmd(UAContext *ua, const char *command);
84
85 int qhelp_cmd(UAContext *ua, const char *cmd);
86 int quit_cmd(UAContext *ua, const char *cmd);
87
88 /* not all in alphabetical order.  New commands are added after existing commands with similar letters
89    to prevent breakage of existing user scripts.  */
90 struct cmdstruct {
91    const char *key;                             /* command */
92    int (*func)(UAContext *ua, const char *cmd); /* handler */
93    const char *help;            /* main purpose */
94    const char *usage;           /* all arguments to build usage */
95    const bool use_in_rs;        /* Can use it in Console RunScript */
96 };
97 static struct cmdstruct commands[] = {                                      /* Can use it in Console RunScript*/
98  { NT_("add"),        add_cmd,     _("Add media to a pool"),   NT_("pool=<pool-name> storage=<storage> jobid=<JobId>"),  false},
99  { NT_("autodisplay"), autodisplay_cmd,_("Autodisplay console messages"), NT_("on | off"),    false},
100  { NT_("automount"),   automount_cmd,  _("Automount after label"),        NT_("on | off"),    false},
101  { NT_("cancel"),     cancel_cmd,    _("Cancel a job"), NT_("jobid=<number-list> | job=<job-name> | ujobid=<unique-jobid> | inactive client=<client-name> storage=<storage-name> | all"), false},
102  { NT_("cloud"),      cloud_cmd,     _("Specific Cloud commands"),
103    NT_("[storage=<storage-name>] [volume=<vol>] [pool=<pool>] [allpools] [allfrompool] [mediatype=<type>] [drive=<number>] [slots=<number] \n"
104        "\tstatus  | prune | list | upload | truncate"), true},
105  { NT_("create"),     create_cmd,    _("Create DB Pool from resource"), NT_("pool=<pool-name>"),                    false},
106  { NT_("delete"),     delete_cmd,    _("Delete volume, pool or job"), NT_("volume=<vol-name> | pool=<pool-name> | jobid=<id> | snapshot"), true},
107  { NT_("disable"),    disable_cmd,   _("Disable a job, attributes batch process"), NT_("job=<name> | client=<name> | schedule=<name> | storage=<name> | batch"),  true},
108  { NT_("enable"),     enable_cmd,    _("Enable a job, attributes batch process"), NT_("job=<name> | client=<name> | schedule=<name> | storage=<name> | batch"),   true},
109  { NT_("estimate"),   estimate_cmd,  _("Performs FileSet estimate, listing gives full listing"),
110    NT_("fileset=<fs> client=<cli> level=<level> accurate=<yes/no> job=<job> listing"), true},
111
112  { NT_("exit"),       quit_cmd,      _("Terminate Bconsole session"), NT_(""),         false},
113  { NT_("gui"),        gui_cmd,       _("Non-interactive gui mode"),   NT_("on | off"), false},
114  { NT_("help"),       help_cmd,      _("Print help on specific command"),
115    NT_("add autodisplay automount cancel create delete disable\n\tenable estimate exit gui label list llist"
116        "\n\tmessages memory mount prune purge quit query\n\trestore relabel release reload run status"
117        "\n\tsetbandwidth setdebug setip show sqlquery time trace unmount\n\tumount update use var version wait"
118        "\n\tsnapshot"),         false},
119
120  { NT_("label"),      label_cmd,     _("Label a tape"), NT_("storage=<storage> volume=<vol> pool=<pool> slot=<slot> drive=<nb> barcodes"), false},
121  { NT_("list"),       list_cmd,      _("List objects from catalog"),
122    NT_("jobs [client=<cli>] [jobid=<nn>] [ujobid=<name>] [job=<name>] [joberrors] [jobstatus=<s>] [level=<l>] [jobtype=<t>] [limit=<n>]|\n"
123        "\tjobtotals | pools | volume | media <pool=pool-name> | files [type=<deleted|all>] jobid=<nn> | copies jobid=<nn> |\n"
124        "\tjoblog jobid=<nn> | pluginrestoreconf jobid=<nn> restoreobjectid=<nn> | snapshot\n"
125       ), false},
126
127  { NT_("llist"),      llist_cmd,     _("Full or long list like list command"),
128    NT_("jobs [client=<cli>] [jobid=<nn>] [ujobid=<name>] [job=<name>] [joberrors] [jobstatus=<s>] [level=<l>] [jobtype=<t>] [order=<asc/desc>] [limit=<n>]|\n"
129        "\tjobtotals | pools | volume | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn> |\n"
130        "\tjoblog jobid=<nn> | pluginrestoreconf jobid=<nn> restoreobjectid=<nn> | snapshot |\n"
131        "\tfileindex=<mm>\n"), false},
132
133  { NT_("messages"),   messagescmd,   _("Display pending messages"),   NT_(""),    false},
134  { NT_("memory"),     memory_cmd,    _("Print current memory usage"), NT_(""),    true},
135  { NT_("mount"),      mount_cmd,     _("Mount storage"),
136    NT_("storage=<storage-name> slot=<num> drive=<num> [ device=<device-name> ] [ jobid=<id> | job=<job-name> ]"), false},
137
138  { NT_("prune"),      prunecmd,      _("Prune expired records from catalog"),
139    NT_("files | jobs | pool=<pool> | snapshot  [client=<client-name>] | client=<client-name> | [ expired ] volume=<volume-name> "), true},
140
141  { NT_("purge"),      purge_cmd,     _("Purge records from catalog"), NT_("files jobs volume=<vol> [mediatype=<type> pool=<pool> allpools storage=<st> drive=<num>]"),  true},
142  { NT_("quit"),       quit_cmd,      _("Terminate Bconsole session"), NT_(""),              false},
143  { NT_("query"),      query_cmd,     _("Query catalog"), NT_("[<query-item-number>]"),      false},
144  { NT_("restore"),    restore_cmd,   _("Restore files"),
145    NT_("where=</path> client=<client> storage=<storage> bootstrap=<file> "
146        "restorejob=<job> restoreclient=<cli>"
147        "\n\tcomment=<text> jobid=<jobid> jobuser=<user> jobgroup=<grp> copies done select all"), false},
148
149  { NT_("relabel"),    relabel_cmd,   _("Relabel a tape"),
150    NT_("storage=<storage-name> oldvolume=<old-volume-name>\n\tvolume=<newvolume-name> pool=<pool>"), false},
151
152  { NT_("release"),    release_cmd,   _("Release storage"),  NT_("storage=<storage-name> [ device=<device-name> ] "),      false},
153  { NT_("reload"),     reload_cmd,    _("Reload conf file"), NT_(""),                  true},
154  { NT_("run"),        run_cmd,       _("Run a job"),
155    NT_("job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
156        " where=<directory-prefix>\n\twhen=<universal-time-specification> pool=<pool-name>\n\t"
157        " nextpool=<next-pool-name> comment=<text> accurate=<bool> spooldata=<bool> yes"), false},
158
159  { NT_("restart"),    restart_cmd,   _("Restart a job"),
160    NT_("incomplete job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
161        "when=<universal-time-specification>\n\tcomment=<text> spooldata=<bool> jobid=<jobid>"), false},
162
163  { NT_("resume"),    restart_cmd,   _("Resume a job"),
164    NT_("incomplete job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
165        "when=<universal-time-specification>\n\tcomment=<text> spooldata=<bool> jobid=<jobid>"), false},
166
167  { NT_("status"),     status_cmd,    _("Report status"),
168    NT_("all | network [bytes=<nn-b>] | dir=<dir-name> | director | client=<client-name> |\n"
169        "\tstorage=<storage-name> slots |\n"
170        "\tschedule [job=<job-name>] [days=<nn>] [limit=<nn>]\n"
171        "\t\t[time=<universal-time-specification>]"), true},
172
173  { NT_("stop"),       cancel_cmd,    _("Stop a job"), NT_("jobid=<number-list> job=<job-name> ujobid=<unique-jobid> all"), false},
174  { NT_("setdebug"),   setdebug_cmd,  _("Sets debug level"),
175    NT_("level=<nn> tags=<tags> trace=0/1 options=<0tTc> tags=<tags> | client=<client-name> | dir | storage=<storage-name> | all"), true},
176
177  { NT_("setbandwidth"),   setbwlimit_cmd,  _("Sets bandwidth"),
178    NT_("limit=<speed> client=<client-name> jobid=<number> job=<job-name> ujobid=<unique-jobid>"), true},
179
180  { NT_("snapshot"),   snapshot_cmd,  _("Handle snapshots"),
181    NT_("[client=<client-name> | job=<job-name> | jobid=<jobid>] [delete | list | listclient | prune | sync | update]"), true},
182
183  { NT_("setip"),      setip_cmd,     _("Sets new client address -- if authorized"), NT_(""),   false},
184  { NT_("show"),       show_cmd,      _("Show resource records"),
185    NT_("job=<xxx> |  pool=<yyy> | fileset=<aaa> | schedule=<sss> | client=<zzz> | storage=<sss> | disabled | all"), true},
186
187  { NT_("sqlquery"),   sqlquery_cmd,  _("Use SQL to query catalog"), NT_(""),          false},
188  { NT_("time"),       time_cmd,      _("Print current time"),       NT_(""),          true},
189  { NT_("trace"),      trace_cmd,     _("Turn on/off trace to file"), NT_("on | off"), true},
190  { NT_("truncate"),   truncate_cmd,  _("Truncate one or more Volumes"), NT_("volume=<vol> [mediatype=<type> pool=<pool> allpools storage=<st> drive=<num>]"),  true},
191  { NT_("unmount"),    unmount_cmd,   _("Unmount storage"),
192    NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
193
194  { NT_("umount"),     unmount_cmd,   _("Umount - for old-time Unix guys, see unmount"),
195    NT_("storage=<storage-name> [ drive=<num> ] [ device=<dev-name> ]| jobid=<id> | job=<job-name>"), false},
196
197  { NT_("update"),     update_cmd,    _("Update volume, pool or stats"),
198    NT_("stats\n\tsnapshot\n\tpool=<poolname>\n\tslots storage=<storage> scan"
199        "\n\tvolume=<volname> volstatus=<status> volretention=<time-def> cacheretention=<time-def>"
200        "\n\t pool=<pool> recycle=<yes/no> slot=<number>\n\t inchanger=<yes/no>"
201        "\n\t maxvolbytes=<size> maxvolfiles=<nb> maxvoljobs=<nb>"
202        "\n\t enabled=<yes/no> recyclepool=<pool> actiononpurge=<action>"
203        "\n\t allfrompool=<pool> fromallpools frompool"),true},
204  { NT_("use"),        use_cmd,       _("Use catalog xxx"), NT_("catalog=<catalog>"),     false},
205  { NT_("var"),        var_cmd,       _("Does variable expansion"), NT_(""),  false},
206  { NT_("version"),    version_cmd,   _("Print Director version"),  NT_(""),  true},
207  { NT_("wait"),       wait_cmd,      _("Wait until no jobs are running"),
208    NT_("jobname=<name> | jobid=<nnn> | ujobid=<complete_name>"), false}
209 };
210
211 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
212
213 const char *get_command(int index) {
214    return commands[index].key;
215 }
216
217 /*
218  * Execute a command from the UA
219  */
220 bool do_a_command(UAContext *ua)
221 {
222    int i;
223    int len;
224    bool ok = false;
225    bool found = false;
226
227    Dmsg1(900, "Command: %s\n", ua->argk[0]);
228    if (ua->argc == 0) {
229       return false;
230    }
231
232    if (ua->jcr->wstorage) {
233       while (ua->jcr->wstorage->size()) {
234          ua->jcr->wstorage->remove(0);
235       }
236    }
237
238    len = strlen(ua->argk[0]);
239    for (i=0; i<comsize; i++) {     /* search for command */
240       if (strncasecmp(ua->argk[0],  commands[i].key, len) == 0) {
241          ua->cmd_index = i;
242          /* Check if command permitted, but "quit" is always OK */
243          if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
244              !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
245             break;
246          }
247          /* Check if this command is authorized in RunScript */
248          if (ua->runscript && !commands[i].use_in_rs) {
249             ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
250             break;
251          }
252          if (ua->api) ua->signal(BNET_CMD_BEGIN);
253          ok = (*commands[i].func)(ua, ua->cmd);   /* go execute command */
254          if (ua->api) ua->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
255          found = ua->UA_sock && ua->UA_sock->is_stop() ? false : true;
256          break;
257       }
258    }
259    if (!found) {
260       ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
261       ok = false;
262    }
263    return ok;
264 }
265
266 /*
267  * This is a common routine used to stuff the Pool DB record defaults
268  *   into the Media DB record just before creating a media (Volume)
269  *   record.
270  */
271 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
272 {
273    mr->PoolId = pr->PoolId;
274    bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
275    mr->Recycle = pr->Recycle;
276    mr->VolRetention = pr->VolRetention;
277    mr->CacheRetention = pr->CacheRetention;
278    mr->VolUseDuration = pr->VolUseDuration;
279    mr->ActionOnPurge = pr->ActionOnPurge;
280    mr->RecyclePoolId = pr->RecyclePoolId;
281    mr->MaxVolJobs = pr->MaxVolJobs;
282    mr->MaxVolFiles = pr->MaxVolFiles;
283    mr->MaxVolBytes = pr->MaxVolBytes;
284    mr->LabelType = pr->LabelType;
285    mr->Enabled = 1;
286 }
287
288
289 /*
290  *  Add Volumes to an existing Pool
291  */
292 static int add_cmd(UAContext *ua, const char *cmd)
293 {
294    POOL_DBR pr;
295    MEDIA_DBR mr;
296    int num, i, max, startnum;
297    char name[MAX_NAME_LENGTH];
298    STORE *store;
299    int Slot = 0, InChanger = 0;
300
301    ua->send_msg(_(
302 "You probably don't want to be using this command since it\n"
303 "creates database records without labeling the Volumes.\n"
304 "You probably want to use the \"label\" command.\n\n"));
305
306    if (!open_client_db(ua)) {
307       return 1;
308    }
309
310    memset(&pr, 0, sizeof(pr));
311
312    if (!get_pool_dbr(ua, &pr)) {
313       return 1;
314    }
315
316    Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
317       pr.MaxVols, pr.PoolType);
318
319    while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
320       ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
321       if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
322          return 1;
323       }
324       pr.MaxVols = ua->pint32_val;
325    }
326
327    /* Get media type */
328    if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
329       bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
330    } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
331       return 1;
332    }
333
334    if (pr.MaxVols == 0) {
335       max = 1000;
336    } else {
337       max = pr.MaxVols - pr.NumVols;
338    }
339    for (;;) {
340       char buf[100];
341       bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
342       if (!get_pint(ua, buf)) {
343          return 1;
344       }
345       num = ua->pint32_val;
346       if (num < 0 || num > max) {
347          ua->warning_msg(_("The number must be between 0 and %d\n"), max);
348          continue;
349       }
350       break;
351    }
352
353    for (;;) {
354       if (num == 0) {
355          if (!get_cmd(ua, _("Enter Volume name: "))) {
356             return 1;
357          }
358       } else {
359          if (!get_cmd(ua, _("Enter base volume name: "))) {
360             return 1;
361          }
362       }
363       /* Don't allow | in Volume name because it is the volume separator character */
364       if (!is_volume_name_legal(ua, ua->cmd)) {
365          continue;
366       }
367       if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
368          ua->warning_msg(_("Volume name too long.\n"));
369          continue;
370       }
371       if (strlen(ua->cmd) == 0) {
372          ua->warning_msg(_("Volume name must be at least one character long.\n"));
373          continue;
374       }
375       break;
376    }
377
378    bstrncpy(name, ua->cmd, sizeof(name));
379    if (num > 0) {
380       bstrncat(name, "%04d", sizeof(name));
381
382       for (;;) {
383          if (!get_pint(ua, _("Enter the starting number: "))) {
384             return 1;
385          }
386          startnum = ua->pint32_val;
387          if (startnum < 1) {
388             ua->warning_msg(_("Start number must be greater than zero.\n"));
389             continue;
390          }
391          break;
392       }
393    } else {
394       startnum = 1;
395       num = 1;
396    }
397
398    if (store && store->autochanger) {
399       if (!get_pint(ua, _("Enter slot (0 for none): "))) {
400          return 1;
401       }
402       Slot = ua->pint32_val;
403       if (!get_yesno(ua, _("InChanger? yes/no: "))) {
404          return 1;
405       }
406       InChanger = ua->pint32_val;
407    }
408
409    set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
410    for (i=startnum; i < num+startnum; i++) {
411       bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
412       mr.Slot = Slot++;
413       mr.InChanger = InChanger;
414       mr.Enabled = 1;
415       set_storageid_in_mr(store, &mr);
416       Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
417       if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
418          ua->error_msg("%s", db_strerror(ua->db));
419          return 1;
420       }
421 //    if (i == startnum) {
422 //       first_id = mr.PoolId;
423 //    }
424    }
425    pr.NumVols += num;
426    Dmsg0(200, "Update pool record.\n");
427    if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
428       ua->warning_msg("%s", db_strerror(ua->db));
429       return 1;
430    }
431    ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
432
433    return 1;
434 }
435
436 /*
437  * Turn auto mount on/off
438  *
439  *  automount on
440  *  automount off
441  */
442 int automount_cmd(UAContext *ua, const char *cmd)
443 {
444    char *onoff;
445
446    if (ua->argc != 2) {
447       if (!get_cmd(ua, _("Turn on or off? "))) {
448             return 1;
449       }
450       onoff = ua->cmd;
451    } else {
452       onoff = ua->argk[1];
453    }
454
455    ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
456    return 1;
457 }
458
459 /*
460  * Cancel/Stop a job -- Stop marks it as Incomplete
461  *   so that it can be restarted.
462  */
463 static int cancel_cmd(UAContext *ua, const char *cmd)
464 {
465    JCR    *jcr;
466    bool    ret = true;
467    int     nb;
468    bool    cancel = strcasecmp(commands[ua->cmd_index].key, "cancel") == 0;
469    alist  *jcrs = New(alist(5, not_owned_by_alist));
470
471    /* If the user explicitely ask, we can send the cancel command to
472     * the FD.
473     */
474    if (find_arg(ua, "inactive") > 0) {
475       ret = cancel_inactive_job(ua);
476       goto bail_out;
477    }
478
479    nb = select_running_jobs(ua, jcrs, commands[ua->cmd_index].key);
480
481    foreach_alist(jcr, jcrs) {
482       /* Execute the cancel command only if we don't have an error */
483       if (nb != -1) {
484          ret &= cancel_job(ua, jcr, 60, cancel);
485       }
486       free_jcr(jcr);
487    }
488
489 bail_out:
490    delete jcrs;
491    return ret;
492 }
493
494 /*
495  * This is a common routine to create or update a
496  *   Pool DB base record from a Pool Resource. We handle
497  *   the setting of MaxVols and NumVols slightly differently
498  *   depending on if we are creating the Pool or we are
499  *   simply bringing it into agreement with the resource (updage).
500  *
501  * Caution : RecyclePoolId isn't setup in this function.
502  *           You can use set_pooldbr_recyclepoolid();
503  *
504  */
505 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
506 {
507    bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
508    if (op == POOL_OP_CREATE) {
509       pr->MaxVols = pool->max_volumes;
510       pr->NumVols = 0;
511    } else {          /* update pool */
512       if (pr->MaxVols != pool->max_volumes) {
513          pr->MaxVols = pool->max_volumes;
514       }
515       if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
516          pr->MaxVols = pr->NumVols;
517       }
518    }
519    pr->LabelType = pool->LabelType;
520    pr->UseOnce = pool->use_volume_once;
521    pr->UseCatalog = pool->use_catalog;
522    pr->Recycle = pool->Recycle;
523    pr->VolRetention = pool->VolRetention;
524    pr->CacheRetention = pool->CacheRetention;
525    pr->VolUseDuration = pool->VolUseDuration;
526    pr->MaxVolJobs = pool->MaxVolJobs;
527    pr->MaxVolFiles = pool->MaxVolFiles;
528    pr->MaxVolBytes = pool->MaxVolBytes;
529    pr->AutoPrune = pool->AutoPrune;
530    pr->ActionOnPurge = pool->action_on_purge;
531    pr->Recycle = pool->Recycle;
532    if (pool->label_format) {
533       bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
534    } else {
535       bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat));    /* none */
536    }
537 }
538
539 /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
540 int update_pool_references(JCR *jcr, BDB *db, POOL *pool)
541 {
542    POOL_DBR  pr;
543
544    if (pool->ScratchPool == pool) {
545       Jmsg(NULL, M_WARNING, 0,
546            _("The ScratchPool directive for Pool \"%s\" is incorrect. Using default Scratch pool instead.\n"),
547            pool->name());
548       pool->ScratchPool = NULL;
549    }
550
551    if (!pool->RecyclePool && !pool->ScratchPool) {
552       return 1;
553    }
554
555    memset(&pr, 0, sizeof(POOL_DBR));
556    bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
557
558    /* Don't compute NumVols here */
559    if (!db_get_pool_record(jcr, db, &pr)) {
560       return -1;                       /* not exists in database */
561    }
562
563    set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
564
565    if (!set_pooldbr_references(jcr, db, &pr, pool)) {
566       return -1;                      /* error */
567    }
568
569    /* NumVols is updated here */
570    if (!db_update_pool_record(jcr, db, &pr)) {
571       return -1;                      /* error */
572    }
573    return 1;
574 }
575
576 /* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource
577  * works with set_pooldbr_from_poolres
578  */
579 bool set_pooldbr_references(JCR *jcr, BDB *db, POOL_DBR *pr, POOL *pool)
580 {
581    POOL_DBR rpool;
582    bool ret = true;
583
584    if (pool->RecyclePool) {
585       memset(&rpool, 0, sizeof(POOL_DBR));
586
587       bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
588       if (db_get_pool_record(jcr, db, &rpool)) {
589         pr->RecyclePoolId = rpool.PoolId;
590       } else {
591         Jmsg(jcr, M_WARNING, 0,
592         _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
593           "Try to update it with 'update pool=%s'\n"),
594         pool->name(), rpool.Name, rpool.Name,pool->name());
595
596         ret = false;
597       }
598    } else {                    /* no RecyclePool used, set it to 0 */
599       pr->RecyclePoolId = 0;
600    }
601
602    if (pool->ScratchPool) {
603       memset(&rpool, 0, sizeof(POOL_DBR));
604
605       bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
606       if (db_get_pool_record(jcr, db, &rpool)) {
607         pr->ScratchPoolId = rpool.PoolId;
608       } else {
609         Jmsg(jcr, M_WARNING, 0,
610         _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
611           "Try to update it with 'update pool=%s'\n"),
612         pool->name(), rpool.Name, rpool.Name,pool->name());
613         ret = false;
614       }
615    } else {                    /* no ScratchPool used, set it to 0 */
616       pr->ScratchPoolId = 0;
617    }
618
619    return ret;
620 }
621
622
623 /*
624  * Create a pool record from a given Pool resource
625  *   Also called from backup.c
626  * Returns: -1  on error
627  *           0  record already exists
628  *           1  record created
629  */
630
631 int create_pool(JCR *jcr, BDB *db, POOL *pool, e_pool_op op)
632 {
633    POOL_DBR  pr;
634    memset(&pr, 0, sizeof(POOL_DBR));
635    bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
636
637    if (db_get_pool_record(jcr, db, &pr)) {
638       /* Pool Exists */
639       if (op == POOL_OP_UPDATE) {  /* update request */
640          set_pooldbr_from_poolres(&pr, pool, op);
641          set_pooldbr_references(jcr, db, &pr, pool);
642          db_update_pool_record(jcr, db, &pr);
643       }
644       return 0;                       /* exists */
645    }
646
647    set_pooldbr_from_poolres(&pr, pool, op);
648    set_pooldbr_references(jcr, db, &pr, pool);
649
650    if (!db_create_pool_record(jcr, db, &pr)) {
651       return -1;                      /* error */
652    }
653    return 1;
654 }
655
656
657
658 /*
659  * Create a Pool Record in the database.
660  *  It is always created from the Resource record.
661  */
662 static int create_cmd(UAContext *ua, const char *cmd)
663 {
664    POOL *pool;
665
666    if (!open_client_db(ua)) {
667       return 1;
668    }
669
670    pool = get_pool_resource(ua);
671    if (!pool) {
672       return 1;
673    }
674
675    switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
676    case 0:
677       ua->error_msg(_("Error: Pool %s already exists.\n"
678                "Use update to change it.\n"), pool->name());
679       break;
680
681    case -1:
682       ua->error_msg("%s", db_strerror(ua->db));
683       break;
684
685    default:
686      break;
687    }
688    ua->send_msg(_("Pool %s created.\n"), pool->name());
689    return 1;
690 }
691
692
693 extern DIRRES *director;
694 extern char *configfile;
695
696 static int setbwlimit_client(UAContext *ua, CLIENT *client, char *Job, int64_t limit)
697 {
698    CLIENT *old_client;
699    char ed1[50];
700    if (!client) {
701       return 1;
702    }
703
704    /* Connect to File daemon */
705    old_client = ua->jcr->client;
706    ua->jcr->client = client;
707    ua->jcr->max_bandwidth = limit;
708
709    /* Try to connect for 15 seconds */
710    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
711       client->name(), client->address(), client->FDport);
712    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
713       ua->error_msg(_("Failed to connect to Client.\n"));
714       goto bail_out;
715    }
716    Dmsg0(120, "Connected to file daemon\n");
717
718    if (!send_bwlimit(ua->jcr, Job)) {
719       ua->error_msg(_("Failed to set bandwidth limit to Client.\n"));
720
721    } else {
722       /* Note, we add 2000 OK that was sent by FD to us to message */
723       ua->info_msg(_("2000 OK Limiting bandwidth to %sB/s %s\n"),
724                    edit_uint64_with_suffix(limit, ed1), *Job?Job:_("on running and future jobs"));
725    }
726
727    ua->jcr->file_bsock->signal(BNET_TERMINATE);
728    free_bsock(ua->jcr->file_bsock);
729    ua->jcr->max_bandwidth = 0;
730
731 bail_out:
732    ua->jcr->client = old_client;
733    return 1;
734 }
735
736 static int setbwlimit_cmd(UAContext *ua, const char *cmd)
737 {
738    int action = -1;
739    CLIENT *client = NULL;
740    char Job[MAX_NAME_LENGTH];
741    *Job=0;
742    uint64_t limit = 0;
743    JCR *jcr = NULL;
744    int i;
745
746    const char *lst_all[] = { "job", "jobid", "jobname", "client", NULL };
747    if (find_arg_keyword(ua, lst_all) < 0) {
748        start_prompt(ua, _("Set Bandwidth choice:\n"));
749        add_prompt(ua, _("Running Job")); /* 0 */
750        add_prompt(ua, _("Running and future Jobs for a Client")); /* 1 */
751        action = do_prompt(ua, "item", _("Choose where to limit the bandwidth"),
752                           NULL, 0);
753        if (action < 0) {
754           return 1;
755        }
756    }
757
758    i = find_arg_with_value(ua, "limit");
759    if (i >= 0) {
760       if (!speed_to_uint64(ua->argv[i], strlen(ua->argv[i]), &limit)) {
761          ua->error_msg(_("Invalid value for limit parameter. Expecting speed.\n"));
762          return 1;
763       }
764    } else {
765       if (!get_cmd(ua, _("Enter new bandwidth limit: "))) {
766          return 1;
767       }
768       if (!speed_to_uint64(ua->cmd, strlen(ua->cmd), &limit)) {
769          ua->error_msg(_("Invalid value for limit parameter. Expecting speed.\n"));
770          return 1;
771       }
772    }
773
774    const char *lst[] = { "job", "jobid", "jobname", NULL };
775    if (action == 0 || find_arg_keyword(ua, lst) > 0) {
776       alist *jcrs = New(alist(10, not_owned_by_alist));
777       select_running_jobs(ua, jcrs, "limit");
778       foreach_alist(jcr, jcrs) {
779          jcr->max_bandwidth = limit; /* TODO: see for locking (Should be safe)*/
780          bstrncpy(Job, jcr->Job, sizeof(Job));
781          client = jcr->client;
782          setbwlimit_client(ua, client, Job, limit);
783          free_jcr(jcr);
784       }
785
786    } else {
787       client = get_client_resource(ua, JT_BACKUP_RESTORE);
788       if (client) {
789          setbwlimit_client(ua, client, Job, limit);
790       }
791    }
792    return 1;
793 }
794
795 /*
796  * Set a new address in a Client resource. We do this only
797  *  if the Console name is the same as the Client name
798  *  and the Console can access the client.
799  */
800 static int setip_cmd(UAContext *ua, const char *cmd)
801 {
802    CLIENT *client;
803    char addr[1024];
804    if (!ua->cons || !acl_access_client_ok(ua, ua->cons->name(), JT_BACKUP_RESTORE)) {
805       ua->error_msg(_("Unauthorized command from this console.\n"));
806       return 1;
807    }
808    LockRes();
809    client = GetClientResWithName(ua->cons->name());
810
811    if (!client) {
812       ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
813       goto get_out;
814    }
815    /* MA Bug 6 remove ifdef */
816    sockaddr_to_ascii(&(ua->UA_sock->client_addr),
817          sizeof(ua->UA_sock->client_addr), addr, sizeof(addr));
818    client->setAddress(bstrdup(addr));
819    ua->send_msg(_("Client \"%s\" address set to %s\n"),
820             client->name(), addr);
821 get_out:
822    UnlockRes();
823    return 1;
824 }
825
826 /*
827  * Does all sorts of enable/disable commands: batch, scheduler (not implemented)
828  *  job, client, schedule, storage
829  */
830 static void do_enable_disable_cmd(UAContext *ua, bool setting)
831 {
832    JOB *job = NULL;
833    CLIENT *client = NULL;
834    SCHED *sched = NULL;
835    int i;
836
837    if (find_arg(ua, NT_("batch")) > 0) {
838       ua->send_msg(_("Job Attributes Insertion %sabled\n"), setting?"en":"dis");
839       db_disable_batch_insert(setting);
840       return;
841    }
842
843    /*
844     * if (find_arg(ua, NT_("scheduler")) > 0) {
845     *    ua->send_msg(_("Job Scheduler %sabled\n"), setting?"en":"dis");
846     *    return;
847     * }
848     */
849
850    i = find_arg(ua, NT_("job"));
851    if (i >= 0) {
852       if (ua->argv[i]) {
853          LockRes();
854          job = GetJobResWithName(ua->argv[i]);
855          UnlockRes();
856       } else {
857          job = select_enable_disable_job_resource(ua, setting);
858          if (!job) {
859             return;
860          }
861       }
862    }
863    if (job) {
864       if (!acl_access_ok(ua, Job_ACL, job->name())) {
865          ua->error_msg(_("Unauthorized command from this console.\n"));
866          return;
867       }
868       job->setEnabled(setting);
869       ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
870    }
871
872    i = find_arg(ua, NT_("client"));
873    if (i >= 0) {
874       if (ua->argv[i]) {
875          LockRes();
876          client = GetClientResWithName(ua->argv[i]);
877          UnlockRes();
878       } else {
879          client = select_enable_disable_client_resource(ua, setting);
880          if (!client) {
881             return;
882          }
883       }
884    }
885    if (client) {
886       if (!acl_access_client_ok(ua, client->name(), JT_BACKUP_RESTORE)) {
887          ua->error_msg(_("Unauthorized command from this console.\n"));
888          return;
889       }
890       client->setEnabled(setting);
891       ua->send_msg(_("Client \"%s\" %sabled\n"), client->name(), setting?"en":"dis");
892    }
893
894    i = find_arg(ua, NT_("schedule"));
895    if (i >= 0) {
896       if (ua->argv[i]) {
897          LockRes();
898          sched = (SCHED *)GetResWithName(R_SCHEDULE, ua->argv[i]);
899          UnlockRes();
900       } else {
901          sched = select_enable_disable_schedule_resource(ua, setting);
902          if (!sched) {
903             return;
904          }
905       }
906    }
907    if (sched) {
908       if (!acl_access_ok(ua, Schedule_ACL, sched->name())) {
909          ua->error_msg(_("Unauthorized command from this console.\n"));
910          return;
911       }
912       sched->setEnabled(setting);
913       ua->send_msg(_("Schedule \"%s\" %sabled\n"), sched->name(), setting?"en":"dis");
914    }
915
916    i = find_arg(ua, NT_("storage"));
917    if (i >= 0) {
918       do_storage_cmd(ua, setting?"enable":"disable");
919    }
920
921    if (i < 0 && !sched && !client && !job) {
922       ua->error_msg(_("You must enter one of the following keywords: job, client, schedule, or storage.\n"));
923    }
924
925    return;
926 }
927
928 static int enable_cmd(UAContext *ua, const char *cmd)
929 {
930    do_enable_disable_cmd(ua, true);
931    return 1;
932 }
933
934 static int disable_cmd(UAContext *ua, const char *cmd)
935 {
936    do_enable_disable_cmd(ua, false);
937    return 1;
938 }
939
940 static void do_dir_setdebug(UAContext *ua, int64_t level, int trace_flag, char *options, int64_t tags)
941 {
942    debug_level = level;
943    debug_level_tags = tags;
944    set_trace(trace_flag);
945    set_debug_flags(options);
946 }
947
948 static void do_storage_setdebug(UAContext *ua, STORE *store,
949                int64_t level, int trace_flag, int hangup, int blowup,
950                char *options, char *tags)
951 {
952    BSOCK *sd;
953    USTORE lstore;
954
955    lstore.store = store;
956    pm_strcpy(lstore.store_source, _("unknown source"));
957    set_wstorage(ua->jcr, &lstore);
958    /* Try connecting for up to 15 seconds */
959    ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
960       store->name(), store->address, store->SDport);
961    if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
962       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
963       return;
964    }
965    Dmsg0(120, _("Connected to storage daemon\n"));
966    sd = ua->jcr->store_bsock;
967    sd->fsend("setdebug=%ld trace=%ld hangup=%ld blowup=%ld options=%s tags=%s\n",
968              (int32_t)level, trace_flag, hangup, blowup, options, NPRTB(tags));
969    if (sd->recv() >= 0) {
970       ua->send_msg("%s", sd->msg);
971    }
972    sd->signal(BNET_TERMINATE);
973    free_bsock(ua->jcr->store_bsock);
974    return;
975 }
976
977 /*
978  * For the client, we have the following values that can be set
979  *  level = debug level
980  *  trace = send debug output to a file
981  *  options = various options for debug or specific FD behavior
982  *  hangup = how many records to send to FD before hanging up
983  *    obviously this is most useful for testing restarting
984  *    failed jobs.
985  *  blowup = how many records to send to FD before blowing up the FD.
986  */
987 static void do_client_setdebug(UAContext *ua, CLIENT *client,
988                int64_t level, int trace, int hangup, int blowup,
989                char *options, char *tags)
990 {
991    CLIENT *old_client;
992    BSOCK *fd;
993
994    /* Connect to File daemon */
995
996    old_client = ua->jcr->client;
997    ua->jcr->client = client;
998    /* Try to connect for 15 seconds */
999    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1000       client->name(), client->address(), client->FDport);
1001    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
1002       ua->error_msg(_("Failed to connect to Client.\n"));
1003       ua->jcr->client = old_client;
1004       return;
1005    }
1006    Dmsg0(120, "Connected to file daemon\n");
1007
1008    fd = ua->jcr->file_bsock;
1009    if (ua->jcr->FDVersion <= 10) {
1010       fd->fsend("setdebug=%ld trace=%d hangup=%d\n",
1011                 (int32_t)level, trace, hangup);
1012    } else {
1013       fd->fsend("setdebug=%ld trace=%d hangup=%d blowup=%d options=%s tags=%s\n",
1014                 (int32_t)level, trace, hangup, blowup, options, NPRTB(tags));
1015    }
1016    if (fd->recv() >= 0) {
1017       ua->send_msg("%s", fd->msg);
1018    }
1019    fd->signal(BNET_TERMINATE);
1020    free_bsock(ua->jcr->file_bsock);
1021    ua->jcr->client = old_client;
1022    return;
1023 }
1024
1025
1026 static void do_all_setdebug(UAContext *ua, int64_t level,
1027                int trace_flag, int hangup, int blowup,
1028                char *options, char *tags)
1029 {
1030    STORE *store, **unique_store;
1031    CLIENT *client, **unique_client;
1032    int i, j, found;
1033    int64_t t=0;
1034
1035    /* Director */
1036    debug_parse_tags(tags, &t);
1037    do_dir_setdebug(ua, level, trace_flag, options, t);
1038
1039    /* Count Storage items */
1040    LockRes();
1041    store = NULL;
1042    i = 0;
1043    foreach_res(store, R_STORAGE) {
1044       i++;
1045    }
1046    unique_store = (STORE **) malloc(i * sizeof(STORE));
1047    /* Find Unique Storage address/port */
1048    store = (STORE *)GetNextRes(R_STORAGE, NULL);
1049    i = 0;
1050    unique_store[i++] = store;
1051    while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
1052       found = 0;
1053       for (j=0; j<i; j++) {
1054          if (strcmp(unique_store[j]->address, store->address) == 0 &&
1055              unique_store[j]->SDport == store->SDport) {
1056             found = 1;
1057             break;
1058          }
1059       }
1060       if (!found) {
1061          unique_store[i++] = store;
1062          Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
1063       }
1064    }
1065    UnlockRes();
1066
1067    /* Call each unique Storage daemon */
1068    for (j=0; j<i; j++) {
1069       do_storage_setdebug(ua, unique_store[j], level, trace_flag,
1070          hangup, blowup, options, tags);
1071    }
1072    free(unique_store);
1073
1074    /* Count Client items */
1075    LockRes();
1076    client = NULL;
1077    i = 0;
1078    foreach_res(client, R_CLIENT) {
1079       i++;
1080    }
1081    unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
1082    /* Find Unique Client address/port */
1083    client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
1084    i = 0;
1085    unique_client[i++] = client;
1086    while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
1087       found = 0;
1088       for (j=0; j<i; j++) {
1089          if (strcmp(unique_client[j]->address(), client->address()) == 0 &&
1090              unique_client[j]->FDport == client->FDport) {
1091             found = 1;
1092             break;
1093          }
1094       }
1095       if (!found) {
1096          unique_client[i++] = client;
1097          Dmsg2(140, "Stuffing: %s:%d\n", client->address(), client->FDport);
1098       }
1099    }
1100    UnlockRes();
1101
1102    /* Call each unique File daemon */
1103    for (j=0; j<i; j++) {
1104       do_client_setdebug(ua, unique_client[j], level, trace_flag,
1105          hangup, blowup, options, tags);
1106    }
1107    free(unique_client);
1108 }
1109
1110 /*
1111  * setdebug level=nn all trace=1/0
1112  */
1113 static int setdebug_cmd(UAContext *ua, const char *cmd)
1114 {
1115    STORE *store;
1116    CLIENT *client;
1117    int64_t level=0, tags=0;
1118    int trace_flag = -1;
1119    int hangup = -1;
1120    int blowup = -1;
1121    int i;
1122    char *tags_str=NULL;
1123    char options[60];
1124
1125    Dmsg1(120, "setdebug:%s:\n", cmd);
1126
1127    *options = 0;
1128    i = find_arg_with_value(ua, "options");
1129    if (i >= 0) {
1130       bstrncpy(options, ua->argv[i], sizeof(options) - 1);
1131    }
1132    level = -1;
1133    i = find_arg_with_value(ua, "level");
1134    if (i >= 0) {
1135       level = str_to_int64(ua->argv[i]);
1136    }
1137    if (level < 0) {
1138       if (!get_pint(ua, _("Enter new debug level: "))) {
1139          return 1;
1140       }
1141       level = ua->pint32_val;
1142    }
1143
1144    /* Better to send the tag string instead of tweaking the level
1145     * in case where we extend the tag or change the representation
1146     */
1147    i = find_arg_with_value(ua, "tags");
1148    if (i > 0) {
1149       tags_str = ua->argv[i];
1150       if (!debug_parse_tags(tags_str, &tags)) {
1151          ua->error_msg(_("Incorrect tags found on command line %s\n"), tags_str);
1152          return 1;
1153       }
1154    }
1155
1156    /* Look for trace flag. -1 => not change */
1157    i = find_arg_with_value(ua, "trace");
1158    if (i >= 0) {
1159       trace_flag = atoi(ua->argv[i]);
1160       if (trace_flag > 0) {
1161          trace_flag = 1;
1162       }
1163    }
1164
1165    /* Look for hangup (debug only) flag. -1 => not change */
1166    i = find_arg_with_value(ua, "hangup");
1167    if (i >= 0) {
1168       hangup = atoi(ua->argv[i]);
1169    }
1170
1171    /* Look for blowup (debug only) flag. -1 => not change */
1172    i = find_arg_with_value(ua, "blowup");
1173    if (i >= 0) {
1174       blowup = atoi(ua->argv[i]);
1175    }
1176
1177    /* General debug? */
1178    for (i=1; i<ua->argc; i++) {
1179       if (strcasecmp(ua->argk[i], "all") == 0) {
1180          do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str);
1181          return 1;
1182       }
1183       if (strcasecmp(ua->argk[i], "dir") == 0 ||
1184           strcasecmp(ua->argk[i], "director") == 0) {
1185          do_dir_setdebug(ua, level, trace_flag, options, tags);
1186          return 1;
1187       }
1188       if (strcasecmp(ua->argk[i], "client") == 0 ||
1189           strcasecmp(ua->argk[i], "fd") == 0) {
1190          client = NULL;
1191          if (ua->argv[i]) {
1192             client = GetClientResWithName(ua->argv[i]);
1193             if (client) {
1194                do_client_setdebug(ua, client, level, trace_flag,
1195                   hangup, blowup, options, tags_str);
1196                return 1;
1197             }
1198          }
1199          client = select_client_resource(ua, JT_BACKUP_RESTORE);
1200          if (client) {
1201             do_client_setdebug(ua, client, level, trace_flag,
1202                hangup, blowup, options, tags_str);
1203             return 1;
1204          }
1205       }
1206
1207       if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1208           strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1209           strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1210          store = NULL;
1211          if (ua->argv[i]) {
1212             store = GetStoreResWithName(ua->argv[i]);
1213             if (store) {
1214                do_storage_setdebug(ua, store, level, trace_flag,
1215                   hangup, blowup, options, tags_str);
1216                return 1;
1217             }
1218          }
1219          store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
1220          if (store) {
1221             do_storage_setdebug(ua, store, level, trace_flag,
1222                hangup, blowup, options, tags_str);
1223             return 1;
1224          }
1225       }
1226    }
1227    /*
1228     * We didn't find an appropriate keyword above, so
1229     * prompt the user.
1230     */
1231    start_prompt(ua, _("Available daemons are: \n"));
1232    add_prompt(ua, _("Director"));
1233    add_prompt(ua, _("Storage"));
1234    add_prompt(ua, _("Client"));
1235    add_prompt(ua, _("All"));
1236    switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1237    case 0:                         /* Director */
1238       do_dir_setdebug(ua, level, trace_flag, options, tags);
1239       break;
1240    case 1:
1241       store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
1242       if (store) {
1243          do_storage_setdebug(ua, store, level, trace_flag, hangup, blowup,
1244             options, tags_str);
1245       }
1246       break;
1247    case 2:
1248       client = select_client_resource(ua, JT_BACKUP_RESTORE);
1249       if (client) {
1250          do_client_setdebug(ua, client, level, trace_flag, hangup, blowup,
1251             options, tags_str);
1252       }
1253       break;
1254    case 3:
1255       do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str);
1256       break;
1257    default:
1258       break;
1259    }
1260    return 1;
1261 }
1262
1263 /*
1264  * Turn debug tracing to file on/off
1265  */
1266 static int trace_cmd(UAContext *ua, const char *cmd)
1267 {
1268    char *onoff;
1269
1270    if (ua->argc != 2) {
1271       if (!get_cmd(ua, _("Turn on or off? "))) {
1272          return 1;
1273       }
1274       onoff = ua->cmd;
1275    } else {
1276       onoff = ua->argk[1];
1277    }
1278
1279    set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1280    return 1;
1281 }
1282
1283 static int var_cmd(UAContext *ua, const char *cmd)
1284 {
1285    POOLMEM *val = get_pool_memory(PM_FNAME);
1286    char *var;
1287
1288    if (!open_client_db(ua)) {
1289       return 1;
1290    }
1291    for (var=ua->cmd; *var != ' '; ) {    /* skip command */
1292       var++;
1293    }
1294    while (*var == ' ') {                 /* skip spaces */
1295       var++;
1296    }
1297    Dmsg1(100, "Var=%s:\n", var);
1298    variable_expansion(ua->jcr, var, &val);
1299    ua->send_msg("%s\n", val);
1300    free_pool_memory(val);
1301    return 1;
1302 }
1303
1304 static int estimate_cmd(UAContext *ua, const char *cmd)
1305 {
1306    JOB *job = NULL;
1307    CLIENT *client = NULL;
1308    FILESET *fileset = NULL;
1309    int listing = 0;
1310    char since[MAXSTRING];
1311    JCR *jcr = ua->jcr;
1312    int accurate=-1;
1313
1314    jcr->setJobType(JT_BACKUP);
1315    jcr->start_time = time(NULL);
1316    jcr->setJobLevel(L_FULL);
1317
1318    for (int i=1; i<ua->argc; i++) {
1319       if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1320           strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1321          if (ua->argv[i]) {
1322             client = GetClientResWithName(ua->argv[i]);
1323             if (!client) {
1324                ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1325                return 1;
1326             }
1327             if (!acl_access_client_ok(ua, client->name(), JT_BACKUP)) {
1328                ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1329                return 1;
1330             }
1331             continue;
1332          } else {
1333             ua->error_msg(_("Client name missing.\n"));
1334             return 1;
1335          }
1336       }
1337       if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1338          if (ua->argv[i]) {
1339             job = GetJobResWithName(ua->argv[i]);
1340             if (!job) {
1341                ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1342                return 1;
1343             }
1344             if (!acl_access_ok(ua, Job_ACL, job->name())) {
1345                ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1346                return 1;
1347             }
1348             continue;
1349          } else {
1350             ua->error_msg(_("Job name missing.\n"));
1351             return 1;
1352          }
1353
1354       }
1355       if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1356          if (ua->argv[i]) {
1357             fileset = GetFileSetResWithName(ua->argv[i]);
1358             if (!fileset) {
1359                ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1360                return 1;
1361             }
1362             if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1363                ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1364                return 1;
1365             }
1366             continue;
1367          } else {
1368             ua->error_msg(_("Fileset name missing.\n"));
1369             return 1;
1370          }
1371       }
1372       if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1373          listing = 1;
1374          continue;
1375       }
1376       if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1377          if (ua->argv[i]) {
1378             if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1379                ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1380                return 1;
1381             }
1382             continue;
1383          } else {
1384             ua->error_msg(_("Level value missing.\n"));
1385             return 1;
1386          }
1387       }
1388       if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1389          if (ua->argv[i]) {
1390             if (!is_yesno(ua->argv[i], &accurate)) {
1391                ua->error_msg(_("Invalid value for accurate. "
1392                                "It must be yes or no.\n"));
1393                return 1;
1394             }
1395             continue;
1396          } else {
1397             ua->error_msg(_("Accurate value missing.\n"));
1398             return 1;
1399          }
1400       }
1401    }
1402    if (!job && !(client && fileset)) {
1403       if (!(job = select_job_resource(ua))) {
1404          return 1;
1405       }
1406    }
1407    if (!job) {
1408       job = GetJobResWithName(ua->argk[1]);
1409       if (!job) {
1410          ua->error_msg(_("No job specified.\n"));
1411          return 1;
1412       }
1413       if (!acl_access_ok(ua, Job_ACL, job->name())) {
1414          ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1415          return 1;
1416       }
1417    }
1418    jcr->job = job;
1419    if (!client) {
1420       client = job->client;
1421    }
1422    if (!fileset) {
1423       fileset = job->fileset;
1424    }
1425    jcr->client = client;
1426    jcr->fileset = fileset;
1427    close_db(ua);
1428    if (job->pool->catalog) {
1429       ua->catalog = job->pool->catalog;
1430    } else {
1431       ua->catalog = client->catalog;
1432    }
1433
1434    if (!open_db(ua)) {
1435       return 1;
1436    }
1437
1438    init_jcr_job_record(jcr);
1439
1440    if (!get_or_create_client_record(jcr)) {
1441       return 1;
1442    }
1443    if (!get_or_create_fileset_record(jcr)) {
1444       return 1;
1445    }
1446
1447    get_level_since_time(ua->jcr, since, sizeof(since));
1448
1449    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1450       jcr->client->name(), jcr->client->address(), jcr->client->FDport);
1451    if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1452       ua->error_msg(_("Failed to connect to Client.\n"));
1453       return 1;
1454    }
1455
1456    /* The level string change if accurate mode is enabled */
1457    if (accurate >= 0) {
1458       jcr->accurate = accurate;
1459    } else {
1460       jcr->accurate = job->accurate;
1461    }
1462
1463    if (!send_level_command(jcr)) {
1464       goto bail_out;
1465    }
1466
1467    if (!send_include_list(jcr)) {
1468       ua->error_msg(_("Error sending include list.\n"));
1469       goto bail_out;
1470    }
1471
1472    if (!send_exclude_list(jcr)) {
1473       ua->error_msg(_("Error sending exclude list.\n"));
1474       goto bail_out;
1475    }
1476
1477    /*
1478     * If the job is in accurate mode, we send the list of
1479     * all files to FD.
1480     */
1481    Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1482    if (!send_accurate_current_files(jcr)) {
1483       goto bail_out;
1484    }
1485
1486    jcr->file_bsock->fsend("estimate listing=%d\n", listing);
1487    while (jcr->file_bsock->recv() >= 0) {
1488       ua->send_msg("%s", jcr->file_bsock->msg);
1489    }
1490
1491 bail_out:
1492    if (jcr->file_bsock) {
1493       jcr->file_bsock->signal(BNET_TERMINATE);
1494       free_bsock(ua->jcr->file_bsock);
1495    }
1496    return 1;
1497 }
1498
1499 /*
1500  * print time
1501  */
1502 static int time_cmd(UAContext *ua, const char *cmd)
1503 {
1504    char sdt[50];
1505    time_t ttime = time(NULL);
1506    struct tm tm;
1507    (void)localtime_r(&ttime, &tm);
1508    strftime(sdt, sizeof(sdt), "%a %d-%b-%Y %H:%M:%S", &tm);
1509    ua->send_msg("%s\n", sdt);
1510    return 1;
1511 }
1512
1513 /*
1514  * reload the conf file
1515  */
1516 extern "C" void reload_config(int sig);
1517
1518 static int reload_cmd(UAContext *ua, const char *cmd)
1519 {
1520    reload_config(1);
1521    return 1;
1522 }
1523
1524 /*
1525  * Delete Pool records (should purge Media with it).
1526  *
1527  *  delete pool=<pool-name>
1528  *  delete volume pool=<pool-name> volume=<name>
1529  *  delete jobid=xxx
1530  */
1531 static int delete_cmd(UAContext *ua, const char *cmd)
1532 {
1533    static const char *keywords[] = {
1534       NT_("volume"),
1535       NT_("pool"),
1536       NT_("jobid"),
1537       NT_("snapshot"),
1538       NULL};
1539
1540    /* Deleting large jobs can take time! */
1541    if (!open_new_client_db(ua)) {
1542       return 1;
1543    }
1544
1545    switch (find_arg_keyword(ua, keywords)) {
1546    case 0:
1547       delete_volume(ua);
1548       return 1;
1549    case 1:
1550       delete_pool(ua);
1551       return 1;
1552    case 2:
1553       int i;
1554       while ((i=find_arg(ua, "jobid")) > 0) {
1555          delete_job(ua);
1556          *ua->argk[i] = 0;         /* zap keyword already visited */
1557       }
1558       return 1;
1559    case 3:
1560       delete_snapshot(ua);
1561       return 1;
1562    default:
1563       break;
1564    }
1565
1566    ua->warning_msg(_(
1567 "In general it is not a good idea to delete either a\n"
1568 "Pool or a Volume since they may contain data.\n\n"));
1569
1570    switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1571    case 0:
1572       delete_volume(ua);
1573       break;
1574    case 1:
1575       delete_pool(ua);
1576       break;
1577    case 2:
1578       delete_job(ua);
1579       return 1;
1580    case 3:
1581       delete_snapshot(ua);
1582       return 1;
1583    default:
1584       ua->warning_msg(_("Nothing done.\n"));
1585       break;
1586    }
1587    return 1;
1588 }
1589
1590 /*
1591  * delete_job has been modified to parse JobID lists like the
1592  * following:
1593  * delete JobID=3,4,6,7-11,14
1594  *
1595  * Thanks to Phil Stracchino for the above addition.
1596  */
1597 static void delete_job(UAContext *ua)
1598 {
1599    int JobId;               /* not JobId_t because it's unsigned and not compatible with sellist */
1600    char buf[256];
1601    sellist sl;
1602
1603    int i = find_arg_with_value(ua, NT_("jobid"));
1604    if (i >= 0) {
1605       if (!sl.set_string(ua->argv[i], true)) {
1606          ua->warning_msg("%s", sl.get_errmsg());
1607          return;
1608       }
1609
1610       if (sl.size() > 25 && (find_arg(ua, "yes") < 0)) {
1611          bsnprintf(buf, sizeof(buf),
1612                    _("Are you sure you want to delete %d JobIds ? (yes/no): "), sl.size());
1613          if (!get_yesno(ua, buf)) {
1614             return;
1615          }
1616       }
1617
1618       foreach_sellist(JobId, &sl) {
1619          do_job_delete(ua, JobId);
1620       }
1621
1622    } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1623       return;
1624
1625    } else {
1626       JobId = ua->int64_val;
1627       do_job_delete(ua, JobId);
1628    }
1629 }
1630
1631 /*
1632  * do_job_delete now performs the actual delete operation atomically
1633  */
1634 static void do_job_delete(UAContext *ua, JobId_t JobId)
1635 {
1636    char ed1[50];
1637
1638    edit_int64(JobId, ed1);
1639    purge_jobs_from_catalog(ua, ed1);
1640    ua->send_msg(_("JobId=%s and associated records deleted from the catalog.\n"), ed1);
1641 }
1642
1643 /*
1644  * Delete media records from database -- dangerous
1645  */
1646 static int delete_volume(UAContext *ua)
1647 {
1648    MEDIA_DBR mr;
1649    char buf[1000];
1650    db_list_ctx lst;
1651
1652    if (!select_media_dbr(ua, &mr)) {
1653       return 1;
1654    }
1655    ua->warning_msg(_("\nThis command will delete volume %s\n"
1656       "and all Jobs saved on that volume from the Catalog\n"),
1657       mr.VolumeName);
1658
1659    if (find_arg(ua, "yes") >= 0) {
1660       ua->pint32_val = 1; /* Have "yes" on command line already" */
1661    } else {
1662       bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1663          mr.VolumeName);
1664       if (!get_yesno(ua, buf)) {
1665          return 1;
1666       }
1667    }
1668    if (!ua->pint32_val) {
1669       return 1;
1670    }
1671
1672    /* If not purged, do it */
1673    if (strcmp(mr.VolStatus, "Purged") != 0) {
1674       if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1675          ua->error_msg(_("Can't list jobs on this volume\n"));
1676          return 1;
1677       }
1678       if (lst.count) {
1679          purge_jobs_from_catalog(ua, lst.list);
1680       }
1681    }
1682
1683    db_delete_media_record(ua->jcr, ua->db, &mr);
1684    return 1;
1685 }
1686
1687 /*
1688  * Delete a pool record from the database -- dangerous
1689  */
1690 static int delete_pool(UAContext *ua)
1691 {
1692    POOL_DBR  pr;
1693    char buf[200];
1694
1695    memset(&pr, 0, sizeof(pr));
1696
1697    if (!get_pool_dbr(ua, &pr)) {
1698       return 1;
1699    }
1700    bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1701       pr.Name);
1702    if (!get_yesno(ua, buf)) {
1703       return 1;
1704    }
1705    if (ua->pint32_val) {
1706       db_delete_pool_record(ua->jcr, ua->db, &pr);
1707    }
1708    return 1;
1709 }
1710
1711 int memory_cmd(UAContext *ua, const char *cmd)
1712 {
1713    garbage_collect_memory();
1714    list_dir_status_header(ua);
1715    sm_dump(false, true);
1716    return 1;
1717 }
1718
1719 static void do_storage_cmd(UAContext *ua, const char *command)
1720 {
1721    USTORE store;
1722    BSOCK *sd;
1723    JCR *jcr = ua->jcr;
1724    char dev_name[MAX_NAME_LENGTH];
1725    int drive, i;
1726    int slot;
1727
1728    if (!open_client_db(ua)) {
1729       return;
1730    }
1731    Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1732
1733    store.store = get_storage_resource(ua, true/*arg is storage*/);
1734    if (!store.store) {
1735       return;
1736    }
1737    pm_strcpy(store.store_source, _("unknown source"));
1738    set_wstorage(jcr, &store);
1739    drive = get_storage_drive(ua, store.store);
1740    /* For the disable/enable command, the slot is not mandatory */
1741    if (strcasecmp(command, "disable") == 0 || strcasecmp(command, "enable") == 0) {
1742       slot = 0;
1743    } else {
1744       slot = get_storage_slot(ua, store.store);
1745    }
1746    /* Users may set a device name directly on the command line */
1747    if ((i = find_arg_with_value(ua, "device")) > 0) {
1748       POOLMEM *errmsg = get_pool_memory(PM_NAME);
1749       if (!is_name_valid(ua->argv[i], &errmsg)) {
1750          ua->error_msg(_("Invalid device name. %s"), errmsg);
1751          free_pool_memory(errmsg);
1752          return;
1753       }
1754       free_pool_memory(errmsg);
1755       bstrncpy(dev_name, ua->argv[i], sizeof(dev_name));
1756
1757    } else {                     /* We take the default device name */
1758       bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1759    }
1760
1761    Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1762       store.store->media_type, store.store->dev_name(), drive);
1763    Dmsg4(120, "Cmd: %s %s drive=%d slot=%d\n", command, dev_name, drive, slot);
1764
1765    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1766       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1767       return;
1768    }
1769    sd = jcr->store_bsock;
1770    bash_spaces(dev_name);
1771    sd->fsend("%s %s drive=%d slot=%d\n", command, dev_name, drive, slot);
1772    while (sd->recv() >= 0) {
1773       ua->send_msg("%s", sd->msg);
1774    }
1775    sd->signal(BNET_TERMINATE);
1776    free_bsock(ua->jcr->store_bsock);
1777 }
1778
1779 /*
1780  * mount [storage=<name>] [drive=nn] [slot=mm]
1781  */
1782 static int mount_cmd(UAContext *ua, const char *cmd)
1783 {
1784    do_storage_cmd(ua, "mount")  ;          /* mount */
1785    return 1;
1786 }
1787
1788
1789 /*
1790  * unmount [storage=<name>] [drive=nn]
1791  */
1792 static int unmount_cmd(UAContext *ua, const char *cmd)
1793 {
1794    do_storage_cmd(ua, "unmount");          /* unmount */
1795    return 1;
1796 }
1797
1798
1799 /*
1800  * release [storage=<name>] [drive=nn]
1801  */
1802 static int release_cmd(UAContext *ua, const char *cmd)
1803 {
1804    do_storage_cmd(ua, "release");          /* release */
1805    return 1;
1806 }
1807
1808 /*
1809  * cloud functions, like to upload cached parts to cloud.
1810  */
1811 int cloud_volumes_cmd(UAContext *ua, const char *cmd, const char *mode)
1812 {
1813    int drive = -1;
1814    int nb = 0;
1815    uint32_t *results = NULL;
1816    MEDIA_DBR mr;
1817    POOL_DBR pr;
1818    BSOCK *sd = NULL;
1819    char storage[MAX_NAME_LENGTH];
1820    const char *action = mode;
1821    memset(&pr, 0, sizeof(pr));
1822
1823    /*
1824     * Look for all volumes that are enabled and
1825     *  have more the 200 bytes.
1826     */
1827    mr.Enabled = 1;
1828    mr.Recycle = -1;             /* All Recycle status */
1829    if (strcmp("prunecache", mode) == 0) {
1830       mr.CacheRetention = 1;
1831       action = "truncate cache";
1832    }
1833
1834    if (!scan_storage_cmd(ua, cmd, false, /* fromallpool*/
1835                          &drive, &mr, &pr, NULL, storage,
1836                          &nb, &results))
1837    {
1838       goto bail_out;
1839    }
1840
1841    if ((sd=open_sd_bsock(ua)) == NULL) {
1842       Dmsg0(100, "Can't open connection to sd\n");
1843       goto bail_out;
1844    }
1845
1846    /*
1847     * Loop over the candidate Volumes and upload parts
1848     */
1849    for (int i=0; i < nb; i++) {
1850       bool ok=false;
1851       mr.clear();
1852       mr.MediaId = results[i];
1853       if (!db_get_media_record(ua->jcr, ua->db, &mr)) {
1854          goto bail_out;
1855       }
1856
1857       /* Protect us from spaces */
1858       bash_spaces(mr.VolumeName);
1859       bash_spaces(mr.MediaType);
1860       bash_spaces(pr.Name);
1861       bash_spaces(storage);
1862
1863       sd->fsend("%s Storage=%s Volume=%s PoolName=%s MediaType=%s "
1864                 "Slot=%d drive=%d CacheRetention=%lld\n",
1865                 action, storage, mr.VolumeName, pr.Name, mr.MediaType,
1866                 mr.Slot, drive, mr.CacheRetention);
1867
1868       unbash_spaces(mr.VolumeName);
1869       unbash_spaces(mr.MediaType);
1870       unbash_spaces(pr.Name);
1871       unbash_spaces(storage);
1872
1873       /* Check for valid response */
1874       while (bget_dirmsg(sd) >= 0) {
1875          if (strncmp(sd->msg, "3000 OK truncate cache", 22) == 0) {
1876             ua->send_msg("%s", sd->msg);
1877             ok = true;
1878
1879          } else if (strncmp(sd->msg, "3000 OK", 7) == 0) {
1880             ua->send_msg(_("The volume \"%s\" has been uploaded\n"), mr.VolumeName);
1881             ok = true;
1882
1883
1884          } else if (strncmp(sd->msg, "39", 2) == 0) {
1885             ua->warning_msg("%s", sd->msg);
1886
1887          } else {
1888             ua->send_msg("%s", sd->msg);
1889          }
1890       }
1891       if (!ok) {
1892          ua->warning_msg(_("Unable to %s for volume \"%s\"\n"), action, mr.VolumeName);
1893       }  
1894    }
1895
1896 bail_out:
1897    close_db(ua);
1898    close_sd_bsock(ua);
1899    ua->jcr->wstore = NULL;
1900    if (results) {
1901       free(results);
1902    }
1903
1904    return 1;
1905 }
1906
1907 /* List volumes in the cloud */
1908 /* TODO: Update the code for .api 2 and llist */
1909 static int cloud_list_cmd(UAContext *ua, const char *cmd)
1910 {
1911    int drive = -1;
1912    int64_t size, mtime;
1913    STORE *store = NULL;
1914    MEDIA_DBR mr;
1915    POOL_DBR pr;
1916    BSOCK *sd = NULL;
1917    char storage[MAX_NAME_LENGTH];
1918    char ed1[50], ed2[50];
1919    bool first=true;
1920    uint32_t maxpart=0, part;
1921    uint64_t maxpart_size=0;
1922    memset(&pr, 0, sizeof(pr));
1923    memset(&mr, 0, sizeof(mr));
1924
1925    /* Look at arguments */
1926    for (int i=1; i<ua->argc; i++) {
1927       if (strcasecmp(ua->argk[i], NT_("volume")) == 0
1928           && is_name_valid(ua->argv[i], NULL)) {
1929          bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
1930
1931       } else if (strcasecmp(ua->argk[i], NT_("drive")) == 0 && ua->argv[i]) {
1932          drive = atoi(ua->argv[i]);
1933       }
1934    }
1935
1936    if (!open_client_db(ua)) {
1937       goto bail_out;
1938    }
1939
1940    /* Choose storage */
1941    ua->jcr->wstore = store = get_storage_resource(ua, false);
1942    if (!store) {
1943       goto bail_out;
1944    }
1945    bstrncpy(storage, store->dev_name(), sizeof(storage));
1946    bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
1947
1948    if ((sd=open_sd_bsock(ua)) == NULL) {
1949       Dmsg0(100, "Can't open connection to SD\n");
1950       goto bail_out;
1951    }
1952
1953    /* Protect us from spaces */
1954    bash_spaces(mr.MediaType);
1955    bash_spaces(storage);
1956    bash_spaces(mr.VolumeName);
1957
1958    sd->fsend("cloudlist Storage=%s Volume=%s MediaType=%s Slot=%d drive=%d\n",
1959              storage, mr.VolumeName,  mr.MediaType, mr.Slot, drive);
1960
1961    if (mr.VolumeName[0]) {      /* Want to list parts */
1962       const char *output_hformat="| %8d | %12sB | %20s |\n";
1963       uint64_t volsize=0;
1964       /* Check for valid response */
1965       while (sd->recv() >= 0) {
1966          if (sscanf(sd->msg, "part=%d size=%lld mtime=%lld", &part, &size, &mtime) != 3) {
1967             if (sd->msg[0] == '3') {
1968                ua->send_msg("%s", sd->msg);
1969             }
1970             continue;
1971          }
1972          /* Print information */
1973          if (first) {
1974             ua->send_msg(_("+----------+---------------+----------------------+\n"));
1975             ua->send_msg(_("|   Part   |     Size      |   MTime              |\n"));
1976             ua->send_msg(_("+----------+---------------+----------------------+\n"));
1977             first=false;
1978          }
1979          if (part > maxpart) {
1980             maxpart = part;
1981             maxpart_size = size;
1982          }
1983          volsize += size;
1984          ua->send_msg(output_hformat, part, edit_uint64_with_suffix(size, ed1), bstrftimes(ed2, sizeof(ed2), mtime));
1985       }
1986       if (!first) {
1987          ua->send_msg(_("+----------+---------------+----------------------+\n"));
1988       }
1989       /* TODO: See if we fix the catalog record directly */
1990       if (db_get_media_record(ua->jcr, ua->db, &mr)) {
1991          POOL_MEM errmsg, tmpmsg;
1992          if (mr.LastPartBytes != maxpart_size) {
1993             Mmsg(tmpmsg, "Error on volume \"%s\". Catalog LastPartBytes mismatch %lld != %lld\n",
1994                  mr.VolumeName, mr.LastPartBytes, maxpart_size);
1995             pm_strcpy(errmsg, tmpmsg.c_str());
1996          }
1997          if (mr.VolCloudParts != maxpart) {
1998             Mmsg(tmpmsg, "Error on volume \"%s\". Catalog VolCloudParts mismatch %ld != %ld\n",
1999                  mr.VolumeName, mr.VolCloudParts, maxpart);
2000             pm_strcpy(errmsg, tmpmsg.c_str());
2001          }
2002          if (strlen(errmsg.c_str()) > 0) {
2003             ua->error_msg("\n%s", errmsg.c_str());
2004          }
2005       }
2006    } else {                     /* TODO: Get the last part if possible? */
2007       const char *output_hformat="| %18s | %9s | %20s | %20s | %12sB |\n";
2008
2009       /* Check for valid response */
2010       while (sd->recv() >= 0) {
2011          if (sscanf(sd->msg, "volume=%127s", mr.VolumeName) != 1) {
2012             if (sd->msg[0] == '3') {
2013                ua->send_msg("%s", sd->msg);
2014             }
2015             continue;
2016          }
2017          unbash_spaces(mr.VolumeName);
2018
2019          mr.MediaId = 0;
2020
2021          if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) {
2022             memset(&pr, 0, sizeof(POOL_DBR));
2023             pr.PoolId = mr.PoolId;
2024             if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
2025                strcpy(pr.Name, "?");
2026             }
2027
2028             if (first) {
2029                ua->send_msg(_("+--------------------+-----------+----------------------+----------------------+---------------+\n"));
2030                ua->send_msg(_("|    Volume Name     |   Status  |     Media Type       |       Pool           |    VolBytes   |\n"));
2031                ua->send_msg(_("+--------------------+-----------+----------------------+----------------------+---------------+\n"));
2032                first=false;
2033             }
2034             /* Print information */
2035             ua->send_msg(output_hformat, mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name,
2036                          edit_uint64_with_suffix(mr.VolBytes, ed1));
2037          }
2038       }
2039       if (!first) {
2040          ua->send_msg(_("+--------------------+-----------+----------------------+----------------------+---------------+\n"));
2041       }
2042    }
2043
2044 bail_out:
2045    close_db(ua);
2046    close_sd_bsock(ua);
2047    ua->jcr->wstore = NULL;
2048    return 1;
2049 }
2050
2051 /* Ask client to create/prune/delete a snapshot via the command line */
2052 static int cloud_cmd(UAContext *ua, const char *cmd)
2053 {
2054    for (int i=0; i<ua->argc; i++) {
2055       if (strcasecmp(ua->argk[i], NT_("upload")) == 0) {
2056          return cloud_volumes_cmd(ua, cmd, "upload");
2057
2058       } else if (strcasecmp(ua->argk[i], NT_("list")) == 0) {
2059          return cloud_list_cmd(ua, cmd);
2060
2061       } else if (strcasecmp(ua->argk[i], NT_("truncate")) == 0) {
2062          return cloud_volumes_cmd(ua, cmd, "truncate cache");
2063
2064       } else if (strcasecmp(ua->argk[i], NT_("status")) == 0) {
2065
2066       } else if (strcasecmp(ua->argk[i], NT_("prune")) == 0) {
2067          return cloud_volumes_cmd(ua, cmd, "prunecache");
2068
2069       } else {
2070          continue;
2071       }
2072    }
2073
2074    for ( ;; ) {
2075
2076       start_prompt(ua, _("Cloud choice: \n"));
2077       add_prompt(ua, _("List Cloud Volumes in the Cloud"));
2078       add_prompt(ua, _("Upload a Volume to the Cloud"));
2079       add_prompt(ua, _("Prune the Cloud Cache"));
2080       add_prompt(ua, _("Truncate a Volume Cache"));
2081       add_prompt(ua, _("Done"));
2082
2083       switch(do_prompt(ua, "", _("Select action to perform on Cloud"), NULL, 0)) {
2084       case 0:                   /* list cloud */
2085          cloud_list_cmd(ua, cmd);
2086          break;
2087       case 1:                   /* upload */
2088          cloud_volumes_cmd(ua, cmd, "upload");
2089          break;
2090       case 2:                   /* Prune cache */
2091          cloud_volumes_cmd(ua, cmd, "prunecache");
2092          break;
2093       case 3:                   /* Truncate cache */
2094          cloud_volumes_cmd(ua, cmd, "truncate cache");
2095          break;
2096       default:
2097          ua->info_msg(_("Selection terminated.\n"));
2098          return 1;
2099       }
2100    }
2101    return 1;
2102 }
2103
2104 /*
2105  * Switch databases
2106  *   use catalog=<name>
2107  */
2108 static int use_cmd(UAContext *ua, const char *cmd)
2109 {
2110    CAT *oldcatalog, *catalog;
2111
2112
2113    close_db(ua);                      /* close any previously open db */
2114    oldcatalog = ua->catalog;
2115
2116    if (!(catalog = get_catalog_resource(ua))) {
2117       ua->catalog = oldcatalog;
2118    } else {
2119       ua->catalog = catalog;
2120    }
2121    if (open_db(ua)) {
2122       ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
2123          ua->catalog->name(), ua->catalog->db_name);
2124    }
2125    return 1;
2126 }
2127
2128 int quit_cmd(UAContext *ua, const char *cmd)
2129 {
2130    ua->quit = true;
2131    return 1;
2132 }
2133
2134 /* Handler to get job status */
2135 static int status_handler(void *ctx, int num_fields, char **row)
2136 {
2137    char *val = (char *)ctx;
2138
2139    if (row[0]) {
2140       *val = row[0][0];
2141    } else {
2142       *val = '?';               /* Unknown by default */
2143    }
2144
2145    return 0;
2146 }
2147
2148 /*
2149  * Wait until no job is running
2150  */
2151 int wait_cmd(UAContext *ua, const char *cmd)
2152 {
2153    JCR *jcr;
2154    int i;
2155    time_t stop_time = 0;
2156
2157    /*
2158     * no args
2159     * Wait until no job is running
2160     */
2161    if (ua->argc == 1) {
2162       bmicrosleep(0, 200000);            /* let job actually start */
2163       for (bool running=true; running; ) {
2164          running = false;
2165          foreach_jcr(jcr) {
2166             if (!jcr->is_internal_job()) {
2167                running = true;
2168                break;
2169             }
2170          }
2171          endeach_jcr(jcr);
2172
2173          if (running) {
2174             bmicrosleep(1, 0);
2175          }
2176       }
2177       return 1;
2178    }
2179
2180    i = find_arg_with_value(ua, NT_("timeout"));
2181    if (i > 0 && ua->argv[i]) {
2182       stop_time = time(NULL) + str_to_int64(ua->argv[i]);
2183    }
2184
2185    /* we have jobid, jobname or ujobid argument */
2186
2187    uint32_t jobid = 0 ;
2188
2189    if (!open_client_db(ua)) {
2190       ua->error_msg(_("ERR: Can't open db\n")) ;
2191       return 1;
2192    }
2193
2194    for (int i=1; i<ua->argc; i++) {
2195       if (strcasecmp(ua->argk[i], "jobid") == 0) {
2196          if (!ua->argv[i]) {
2197             break;
2198          }
2199          jobid = str_to_int64(ua->argv[i]);
2200          break;
2201       } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
2202                  strcasecmp(ua->argk[i], "job") == 0) {
2203          if (!ua->argv[i]) {
2204             break;
2205          }
2206          jcr=get_jcr_by_partial_name(ua->argv[i]) ;
2207          if (jcr) {
2208             jobid = jcr->JobId ;
2209             free_jcr(jcr);
2210          }
2211          break;
2212       } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
2213          if (!ua->argv[i]) {
2214             break;
2215          }
2216          jcr=get_jcr_by_full_name(ua->argv[i]) ;
2217          if (jcr) {
2218             jobid = jcr->JobId ;
2219             free_jcr(jcr);
2220          }
2221          break;
2222       /* Wait for a mount request */
2223       } else if (strcasecmp(ua->argk[i], "mount") == 0) {
2224          for (bool waiting=false; !waiting; ) {
2225             foreach_jcr(jcr) {
2226                if (!jcr->is_internal_job() &&
2227                    (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount ||
2228                     jcr->SDJobStatus == JS_WaitMedia || jcr->SDJobStatus == JS_WaitMount))
2229                {
2230                   waiting = true;
2231                   break;
2232                }
2233             }
2234             endeach_jcr(jcr);
2235             if (waiting) {
2236                break;
2237             }
2238             if (stop_time && (time(NULL) >= stop_time)) {
2239                ua->warning_msg(_("Wait on mount timed out\n"));
2240                return 1;
2241             }
2242             bmicrosleep(1, 0);
2243          }
2244          return 1;
2245       }
2246    }
2247
2248    if (jobid == 0) {
2249       ua->error_msg(_("ERR: Job was not found\n"));
2250       return 1 ;
2251    }
2252
2253    /*
2254     * We wait the end of a specific job
2255     */
2256
2257    bmicrosleep(0, 200000);            /* let job actually start */
2258    for (bool running=true; running; ) {
2259       running = false;
2260
2261       jcr=get_jcr_by_id(jobid) ;
2262
2263       if (jcr) {
2264          running = true ;
2265          free_jcr(jcr);
2266       }
2267
2268       if (running) {
2269          bmicrosleep(1, 0);
2270       }
2271    }
2272
2273    /*
2274     * We have to get JobStatus
2275     */
2276
2277    int status ;
2278    char jobstatus = '?';        /* Unknown by default */
2279    char buf[256] ;
2280
2281    bsnprintf(buf, sizeof(buf),
2282              "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
2283
2284
2285    db_sql_query(ua->db, buf, status_handler, (void *)&jobstatus);
2286
2287    switch (jobstatus) {
2288    case JS_Error:
2289       status = 1 ;         /* Warning */
2290       break;
2291
2292    case JS_Incomplete:
2293    case JS_FatalError:
2294    case JS_ErrorTerminated:
2295    case JS_Canceled:
2296       status = 2 ;         /* Critical */
2297       break;
2298
2299    case JS_Warnings:
2300    case JS_Terminated:
2301       status = 0 ;         /* Ok */
2302       break;
2303
2304    default:
2305       status = 3 ;         /* Unknown */
2306       break;
2307    }
2308
2309    ua->send_msg("JobId=%i\n", jobid) ;
2310    ua->send_msg("JobStatus=%s (%c)\n",
2311                 job_status_to_str(jobstatus, 0),
2312                 jobstatus) ;
2313
2314    if (ua->gui || ua->api) {
2315       ua->send_msg("ExitStatus=%i\n", status) ;
2316    }
2317
2318    return 1;
2319 }
2320
2321
2322 static int help_cmd(UAContext *ua, const char *cmd)
2323 {
2324    int i;
2325    ua->send_msg(_("  Command       Description\n  =======       ===========\n"));
2326    for (i=0; i<comsize; i++) {
2327       if (ua->argc == 2) {
2328          if (!strcasecmp(ua->argk[1], commands[i].key)) {
2329             ua->send_msg(_("  %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
2330                          commands[i].help, commands[i].usage);
2331             break;
2332          }
2333       } else {
2334          ua->send_msg(_("  %-13s %s\n"), commands[i].key, commands[i].help);
2335       }
2336    }
2337    if (i == comsize && ua->argc == 2) {
2338       ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
2339    }
2340    ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
2341    return 1;
2342 }
2343
2344 int qhelp_cmd(UAContext *ua, const char *cmd)
2345 {
2346    int i,j;
2347    /* Want to display only commands */
2348    j = find_arg(ua, NT_("all"));
2349    if (j >= 0) {
2350       for (i=0; i<comsize; i++) {
2351          ua->send_msg("%s\n", commands[i].key);
2352       }
2353       return 1;
2354    }
2355    /* Want to display a specific help section */
2356    j = find_arg_with_value(ua, NT_("item"));
2357    if (j >= 0 && ua->argk[j]) {
2358       for (i=0; i<comsize; i++) {
2359          if (bstrcmp(commands[i].key, ua->argv[j])) {
2360             ua->send_msg("%s\n", commands[i].usage);
2361             break;
2362          }
2363       }
2364       return 1;
2365    }
2366    /* Want to display everything */
2367    for (i=0; i<comsize; i++) {
2368       ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
2369    }
2370    return 1;
2371 }
2372
2373 #if 1
2374 static int version_cmd(UAContext *ua, const char *cmd)
2375 {
2376    ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
2377                 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
2378    return 1;
2379 }
2380 #else
2381 /*
2382  *  Test code -- turned on only for debug testing
2383  */
2384 static int version_cmd(UAContext *ua, const char *cmd)
2385 {
2386    dbid_list ids;
2387    POOL_MEM query(PM_MESSAGE);
2388    open_db(ua);
2389    Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
2390    db_get_query_dbids(ua->jcr, ua->db, query, ids);
2391    ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
2392    for (int i=0; i < ids.num_ids; i++) {
2393       ua->send_msg("id=%d\n", ids.DBId[i]);
2394    }
2395    close_db(ua);
2396    return 1;
2397 }
2398 #endif
2399
2400 /*
2401  * This call uses open_client_db() and force a
2402  * new dedicated connection to the catalog
2403  */
2404 bool open_new_client_db(UAContext *ua)
2405 {
2406    bool ret;
2407
2408    /* Force a new dedicated connection */
2409    ua->force_mult_db_connections = true;
2410    ret = open_client_db(ua);
2411    ua->force_mult_db_connections = false;
2412
2413    return ret;
2414 }
2415
2416 /*
2417  * This call explicitly checks for a catalog=xxx and
2418  *  if given, opens that catalog.  It also checks for
2419  *  client=xxx and if found, opens the catalog
2420  *  corresponding to that client. If we still don't
2421  *  have a catalog, look for a Job keyword and get the
2422  *  catalog from its client record.
2423  */
2424 bool open_client_db(UAContext *ua)
2425 {
2426    int i;
2427    CAT *catalog;
2428    CLIENT *client;
2429    JOB *job;
2430
2431    /* Try for catalog keyword */
2432    i = find_arg_with_value(ua, NT_("catalog"));
2433    if (i >= 0) {
2434       if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
2435          ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
2436          return false;
2437       }
2438       catalog = GetCatalogResWithName(ua->argv[i]);
2439       if (catalog) {
2440          if (ua->catalog && ua->catalog != catalog) {
2441             close_db(ua);
2442          }
2443          ua->catalog = catalog;
2444          return open_db(ua);
2445       }
2446    }
2447
2448    /* Try for client keyword */
2449    i = find_arg_with_value(ua, NT_("client"));
2450    if (i >= 0) {
2451       if (!acl_access_client_ok(ua, ua->argv[i], JT_BACKUP_RESTORE)) {
2452          ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2453          return false;
2454       }
2455       client = GetClientResWithName(ua->argv[i]);
2456       if (client) {
2457          catalog = client->catalog;
2458          if (ua->catalog && ua->catalog != catalog) {
2459             close_db(ua);
2460          }
2461          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2462             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2463             return false;
2464          }
2465          ua->catalog = catalog;
2466          return open_db(ua);
2467       }
2468    }
2469
2470    /* Try for Job keyword */
2471    i = find_arg_with_value(ua, NT_("job"));
2472    if (i >= 0) {
2473       if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2474          ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2475          return false;
2476       }
2477       job = GetJobResWithName(ua->argv[i]);
2478       if (job) {
2479          catalog = job->client->catalog;
2480          if (ua->catalog && ua->catalog != catalog) {
2481             close_db(ua);
2482          }
2483          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2484             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2485             return false;
2486          }
2487          ua->catalog = catalog;
2488          return open_db(ua);
2489       }
2490    }
2491
2492    return open_db(ua);
2493 }
2494
2495
2496 /*
2497  * Open the catalog database.
2498  */
2499 bool open_db(UAContext *ua)
2500 {
2501    bool mult_db_conn;
2502
2503    /* With a restricted console, we can't share a SQL connection */
2504    if (ua->cons) {
2505       ua->force_mult_db_connections = true;
2506    }
2507
2508    /* The force_mult_db_connections is telling us if we modify the
2509     * private or the shared link
2510     */
2511    if (ua->force_mult_db_connections) {
2512       ua->db = ua->private_db;
2513
2514    } else {
2515       ua->db = ua->shared_db;
2516    }
2517
2518    if (ua->db) {
2519       return true;
2520    }
2521
2522    if (!ua->catalog) {
2523       ua->catalog = get_catalog_resource(ua);
2524       if (!ua->catalog) {
2525          ua->error_msg( _("Could not find a Catalog resource\n"));
2526          return false;
2527       }
2528    }
2529
2530    /* Some modules like bvfs need their own catalog connection */
2531    mult_db_conn = ua->catalog->mult_db_connections;
2532    if (ua->force_mult_db_connections) {
2533       mult_db_conn = true;
2534    }
2535
2536    ua->jcr->catalog = ua->catalog;
2537
2538    Dmsg0(100, "UA Open database\n");
2539    ua->db = db_init_database(ua->jcr, ua->catalog->db_driver,
2540                              ua->catalog->db_name,
2541                              ua->catalog->db_user,
2542                              ua->catalog->db_password, ua->catalog->db_address,
2543                              ua->catalog->db_port, ua->catalog->db_socket,
2544                              ua->catalog->db_ssl_mode, ua->catalog->db_ssl_key,
2545                              ua->catalog->db_ssl_cert, ua->catalog->db_ssl_ca,
2546                              ua->catalog->db_ssl_capath, ua->catalog->db_ssl_cipher,
2547                              mult_db_conn, ua->catalog->disable_batch_insert); 
2548    if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2549       ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2550                  ua->catalog->db_name);
2551       if (ua->db) {
2552          ua->error_msg("%s", db_strerror(ua->db));
2553       }
2554       close_db(ua);
2555       return false;
2556    }
2557    ua->jcr->db = ua->db;
2558
2559    /* Depending on the type of connection, we set the right variable */
2560    if (ua->force_mult_db_connections) {
2561       ua->private_db = ua->db;
2562
2563    } else {
2564       ua->shared_db = ua->db;
2565    }
2566    /* With a restricted console, the DB backend should know restrictions about
2567     * Pool, Job, etc...
2568     */
2569    if (ua->cons) {
2570       ua->db->set_acl(ua->jcr, DB_ACL_JOB, ua->cons->ACL_lists[Job_ACL]);
2571       ua->db->set_acl(ua->jcr, DB_ACL_CLIENT, ua->cons->ACL_lists[Client_ACL]);
2572       ua->db->set_acl(ua->jcr, DB_ACL_POOL, ua->cons->ACL_lists[Pool_ACL]);
2573       ua->db->set_acl(ua->jcr, DB_ACL_FILESET, ua->cons->ACL_lists[FileSet_ACL]);
2574
2575       /* For RestoreClient and BackupClient, we take also in account the Client list */
2576       ua->db->set_acl(ua->jcr, DB_ACL_RCLIENT,
2577                       ua->cons->ACL_lists[Client_ACL],
2578                       ua->cons->ACL_lists[RestoreClient_ACL]);
2579
2580       ua->db->set_acl(ua->jcr, DB_ACL_BCLIENT,
2581                       ua->cons->ACL_lists[Client_ACL],
2582                       ua->cons->ACL_lists[BackupClient_ACL]);
2583    }
2584    if (!ua->api) {
2585       ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2586    }
2587    Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2588    return true;
2589 }
2590
2591 void close_db(UAContext *ua)
2592 {
2593    if (ua->jcr) {
2594       ua->jcr->db = NULL;
2595    }
2596
2597    if (ua->shared_db) {
2598       db_close_database(ua->jcr, ua->shared_db);
2599       ua->shared_db = NULL;
2600    }
2601
2602    if (ua->private_db) {
2603       db_close_database(ua->jcr, ua->private_db);
2604       ua->private_db = NULL;
2605    }
2606
2607    ua->db = NULL;
2608 }