]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_cmds.c
Add SSL connections to database (PostgreSQL) open code
[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    if (slot < 0) {
1747       return;
1748    }
1749    /* Users may set a device name directly on the command line */
1750    if ((i = find_arg_with_value(ua, "device")) > 0) {
1751       POOLMEM *errmsg = get_pool_memory(PM_NAME);
1752       if (!is_name_valid(ua->argv[i], &errmsg)) {
1753          ua->error_msg(_("Invalid device name. %s"), errmsg);
1754          free_pool_memory(errmsg);
1755          return;
1756       }
1757       free_pool_memory(errmsg);
1758       bstrncpy(dev_name, ua->argv[i], sizeof(dev_name));
1759
1760    } else {                     /* We take the default device name */
1761       bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1762    }
1763
1764    Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1765       store.store->media_type, store.store->dev_name(), drive);
1766    Dmsg4(120, "Cmd: %s %s drive=%d slot=%d\n", command, dev_name, drive, slot);
1767
1768    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1769       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1770       return;
1771    }
1772    sd = jcr->store_bsock;
1773    bash_spaces(dev_name);
1774    sd->fsend("%s %s drive=%d slot=%d\n", command, dev_name, drive, slot);
1775    while (sd->recv() >= 0) {
1776       ua->send_msg("%s", sd->msg);
1777    }
1778    sd->signal(BNET_TERMINATE);
1779    free_bsock(ua->jcr->store_bsock);
1780 }
1781
1782 /*
1783  * mount [storage=<name>] [drive=nn] [slot=mm]
1784  */
1785 static int mount_cmd(UAContext *ua, const char *cmd)
1786 {
1787    do_storage_cmd(ua, "mount")  ;          /* mount */
1788    return 1;
1789 }
1790
1791
1792 /*
1793  * unmount [storage=<name>] [drive=nn]
1794  */
1795 static int unmount_cmd(UAContext *ua, const char *cmd)
1796 {
1797    do_storage_cmd(ua, "unmount");          /* unmount */
1798    return 1;
1799 }
1800
1801
1802 /*
1803  * release [storage=<name>] [drive=nn]
1804  */
1805 static int release_cmd(UAContext *ua, const char *cmd)
1806 {
1807    do_storage_cmd(ua, "release");          /* release */
1808    return 1;
1809 }
1810
1811 /*
1812  * cloud functions, like to upload cached parts to cloud.
1813  */
1814 int cloud_volumes_cmd(UAContext *ua, const char *cmd, const char *mode)
1815 {
1816    int drive = -1;
1817    int nb = 0;
1818    uint32_t *results = NULL;
1819    MEDIA_DBR mr;
1820    POOL_DBR pr;
1821    BSOCK *sd = NULL;
1822    char storage[MAX_NAME_LENGTH];
1823    const char *action = mode;
1824    memset(&pr, 0, sizeof(pr));
1825
1826    /*
1827     * Look for all volumes that are enabled and
1828     *  have more the 200 bytes.
1829     */
1830    mr.Enabled = 1;
1831    mr.Recycle = -1;             /* All Recycle status */
1832    if (strcmp("prunecache", mode) == 0) {
1833       mr.CacheRetention = 1;
1834       action = "truncate cache";
1835    }
1836
1837    if (!scan_storage_cmd(ua, cmd, false, /* fromallpool*/
1838                          &drive, &mr, &pr, NULL, storage,
1839                          &nb, &results))
1840    {
1841       goto bail_out;
1842    }
1843
1844    if ((sd=open_sd_bsock(ua)) == NULL) {
1845       Dmsg0(100, "Can't open connection to sd\n");
1846       goto bail_out;
1847    }
1848
1849    /*
1850     * Loop over the candidate Volumes and upload parts
1851     */
1852    for (int i=0; i < nb; i++) {
1853       bool ok=false;
1854       mr.clear();
1855       mr.MediaId = results[i];
1856       if (!db_get_media_record(ua->jcr, ua->db, &mr)) {
1857          goto bail_out;
1858       }
1859
1860       /* Protect us from spaces */
1861       bash_spaces(mr.VolumeName);
1862       bash_spaces(mr.MediaType);
1863       bash_spaces(pr.Name);
1864       bash_spaces(storage);
1865
1866       sd->fsend("%s Storage=%s Volume=%s PoolName=%s MediaType=%s "
1867                 "Slot=%d drive=%d CacheRetention=%lld\n",
1868                 action, storage, mr.VolumeName, pr.Name, mr.MediaType,
1869                 mr.Slot, drive, mr.CacheRetention);
1870
1871       unbash_spaces(mr.VolumeName);
1872       unbash_spaces(mr.MediaType);
1873       unbash_spaces(pr.Name);
1874       unbash_spaces(storage);
1875
1876       /* Check for valid response */
1877       while (bget_dirmsg(sd) >= 0) {
1878          if (strncmp(sd->msg, "3000 OK truncate cache", 22) == 0) {
1879             ua->send_msg("%s", sd->msg);
1880             ok = true;
1881
1882          } else if (strncmp(sd->msg, "3000 OK", 7) == 0) {
1883             ua->send_msg(_("The volume \"%s\" has been uploaded\n"), mr.VolumeName);
1884             ok = true;
1885
1886
1887          } else if (strncmp(sd->msg, "39", 2) == 0) {
1888             ua->warning_msg("%s", sd->msg);
1889
1890          } else {
1891             ua->send_msg("%s", sd->msg);
1892          }
1893       }
1894       if (!ok) {
1895          ua->warning_msg(_("Unable to %s for volume \"%s\"\n"), action, mr.VolumeName);
1896       }  
1897    }
1898
1899 bail_out:
1900    close_db(ua);
1901    close_sd_bsock(ua);
1902    ua->jcr->wstore = NULL;
1903    if (results) {
1904       free(results);
1905    }
1906
1907    return 1;
1908 }
1909
1910 /* List volumes in the cloud */
1911 /* TODO: Update the code for .api 2 and llist */
1912 static int cloud_list_cmd(UAContext *ua, const char *cmd)
1913 {
1914    int drive = -1;
1915    int64_t size, mtime;
1916    STORE *store = NULL;
1917    MEDIA_DBR mr;
1918    POOL_DBR pr;
1919    BSOCK *sd = NULL;
1920    char storage[MAX_NAME_LENGTH];
1921    char ed1[50], ed2[50];
1922    bool first=true;
1923    uint32_t maxpart=0, part;
1924    uint64_t maxpart_size=0;
1925    memset(&pr, 0, sizeof(pr));
1926    memset(&mr, 0, sizeof(mr));
1927
1928    /* Look at arguments */
1929    for (int i=1; i<ua->argc; i++) {
1930       if (strcasecmp(ua->argk[i], NT_("volume")) == 0
1931           && is_name_valid(ua->argv[i], NULL)) {
1932          bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
1933
1934       } else if (strcasecmp(ua->argk[i], NT_("drive")) == 0 && ua->argv[i]) {
1935          drive = atoi(ua->argv[i]);
1936       }
1937    }
1938
1939    if (!open_client_db(ua)) {
1940       goto bail_out;
1941    }
1942
1943    /* Choose storage */
1944    ua->jcr->wstore = store = get_storage_resource(ua, false);
1945    if (!store) {
1946       goto bail_out;
1947    }
1948    bstrncpy(storage, store->dev_name(), sizeof(storage));
1949    bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
1950
1951    if ((sd=open_sd_bsock(ua)) == NULL) {
1952       Dmsg0(100, "Can't open connection to SD\n");
1953       goto bail_out;
1954    }
1955
1956    /* Protect us from spaces */
1957    bash_spaces(mr.MediaType);
1958    bash_spaces(storage);
1959    bash_spaces(mr.VolumeName);
1960
1961    sd->fsend("cloudlist Storage=%s Volume=%s MediaType=%s Slot=%d drive=%d\n",
1962              storage, mr.VolumeName,  mr.MediaType, mr.Slot, drive);
1963
1964    if (mr.VolumeName[0]) {      /* Want to list parts */
1965       const char *output_hformat="| %8d | %12sB | %20s |\n";
1966       uint64_t volsize=0;
1967       /* Check for valid response */
1968       while (sd->recv() >= 0) {
1969          if (sscanf(sd->msg, "part=%d size=%lld mtime=%lld", &part, &size, &mtime) != 3) {
1970             if (sd->msg[0] == '3') {
1971                ua->send_msg("%s", sd->msg);
1972             }
1973             continue;
1974          }
1975          /* Print information */
1976          if (first) {
1977             ua->send_msg(_("+----------+---------------+----------------------+\n"));
1978             ua->send_msg(_("|   Part   |     Size      |   MTime              |\n"));
1979             ua->send_msg(_("+----------+---------------+----------------------+\n"));
1980             first=false;
1981          }
1982          if (part > maxpart) {
1983             maxpart = part;
1984             maxpart_size = size;
1985          }
1986          volsize += size;
1987          ua->send_msg(output_hformat, part, edit_uint64_with_suffix(size, ed1), bstrftimes(ed2, sizeof(ed2), mtime));
1988       }
1989       if (!first) {
1990          ua->send_msg(_("+----------+---------------+----------------------+\n"));
1991       }
1992       /* TODO: See if we fix the catalog record directly */
1993       if (db_get_media_record(ua->jcr, ua->db, &mr)) {
1994          POOL_MEM errmsg, tmpmsg;
1995          if (mr.LastPartBytes != maxpart_size) {
1996             Mmsg(tmpmsg, "Error on volume \"%s\". Catalog LastPartBytes mismatch %lld != %lld\n",
1997                  mr.VolumeName, mr.LastPartBytes, maxpart_size);
1998             pm_strcpy(errmsg, tmpmsg.c_str());
1999          }
2000          if (mr.VolCloudParts != maxpart) {
2001             Mmsg(tmpmsg, "Error on volume \"%s\". Catalog VolCloudParts mismatch %ld != %ld\n",
2002                  mr.VolumeName, mr.VolCloudParts, maxpart);
2003             pm_strcpy(errmsg, tmpmsg.c_str());
2004          }
2005          if (strlen(errmsg.c_str()) > 0) {
2006             ua->error_msg("\n%s", errmsg.c_str());
2007          }
2008       }
2009    } else {                     /* TODO: Get the last part if possible? */
2010       const char *output_hformat="| %18s | %9s | %20s | %20s | %12sB |\n";
2011
2012       /* Check for valid response */
2013       while (sd->recv() >= 0) {
2014          if (sscanf(sd->msg, "volume=%127s", mr.VolumeName) != 1) {
2015             if (sd->msg[0] == '3') {
2016                ua->send_msg("%s", sd->msg);
2017             }
2018             continue;
2019          }
2020          unbash_spaces(mr.VolumeName);
2021
2022          mr.MediaId = 0;
2023
2024          if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) {
2025             memset(&pr, 0, sizeof(POOL_DBR));
2026             pr.PoolId = mr.PoolId;
2027             if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
2028                strcpy(pr.Name, "?");
2029             }
2030
2031             if (first) {
2032                ua->send_msg(_("+--------------------+-----------+----------------------+----------------------+---------------+\n"));
2033                ua->send_msg(_("|    Volume Name     |   Status  |     Media Type       |       Pool           |    VolBytes   |\n"));
2034                ua->send_msg(_("+--------------------+-----------+----------------------+----------------------+---------------+\n"));
2035                first=false;
2036             }
2037             /* Print information */
2038             ua->send_msg(output_hformat, mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name,
2039                          edit_uint64_with_suffix(mr.VolBytes, ed1));
2040          }
2041       }
2042       if (!first) {
2043          ua->send_msg(_("+--------------------+-----------+----------------------+----------------------+---------------+\n"));
2044       }
2045    }
2046
2047 bail_out:
2048    close_db(ua);
2049    close_sd_bsock(ua);
2050    ua->jcr->wstore = NULL;
2051    return 1;
2052 }
2053
2054 /* Ask client to create/prune/delete a snapshot via the command line */
2055 static int cloud_cmd(UAContext *ua, const char *cmd)
2056 {
2057    for (int i=0; i<ua->argc; i++) {
2058       if (strcasecmp(ua->argk[i], NT_("upload")) == 0) {
2059          return cloud_volumes_cmd(ua, cmd, "upload");
2060
2061       } else if (strcasecmp(ua->argk[i], NT_("list")) == 0) {
2062          return cloud_list_cmd(ua, cmd);
2063
2064       } else if (strcasecmp(ua->argk[i], NT_("truncate")) == 0) {
2065          return cloud_volumes_cmd(ua, cmd, "truncate cache");
2066
2067       } else if (strcasecmp(ua->argk[i], NT_("status")) == 0) {
2068
2069       } else if (strcasecmp(ua->argk[i], NT_("prune")) == 0) {
2070          return cloud_volumes_cmd(ua, cmd, "prunecache");
2071
2072       } else {
2073          continue;
2074       }
2075    }
2076
2077    for ( ;; ) {
2078
2079       start_prompt(ua, _("Cloud choice: \n"));
2080       add_prompt(ua, _("List Cloud Volumes in the Cloud"));
2081       add_prompt(ua, _("Upload a Volume to the Cloud"));
2082       add_prompt(ua, _("Prune the Cloud Cache"));
2083       add_prompt(ua, _("Truncate a Volume Cache"));
2084       add_prompt(ua, _("Done"));
2085
2086       switch(do_prompt(ua, "", _("Select action to perform on Cloud"), NULL, 0)) {
2087       case 0:                   /* list cloud */
2088          cloud_list_cmd(ua, cmd);
2089          break;
2090       case 1:                   /* upload */
2091          cloud_volumes_cmd(ua, cmd, "upload");
2092          break;
2093       case 2:                   /* Prune cache */
2094          cloud_volumes_cmd(ua, cmd, "prunecache");
2095          break;
2096       case 3:                   /* Truncate cache */
2097          cloud_volumes_cmd(ua, cmd, "truncate cache");
2098          break;
2099       default:
2100          ua->info_msg(_("Selection terminated.\n"));
2101          return 1;
2102       }
2103    }
2104    return 1;
2105 }
2106
2107 /*
2108  * Switch databases
2109  *   use catalog=<name>
2110  */
2111 static int use_cmd(UAContext *ua, const char *cmd)
2112 {
2113    CAT *oldcatalog, *catalog;
2114
2115
2116    close_db(ua);                      /* close any previously open db */
2117    oldcatalog = ua->catalog;
2118
2119    if (!(catalog = get_catalog_resource(ua))) {
2120       ua->catalog = oldcatalog;
2121    } else {
2122       ua->catalog = catalog;
2123    }
2124    if (open_db(ua)) {
2125       ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
2126          ua->catalog->name(), ua->catalog->db_name);
2127    }
2128    return 1;
2129 }
2130
2131 int quit_cmd(UAContext *ua, const char *cmd)
2132 {
2133    ua->quit = true;
2134    return 1;
2135 }
2136
2137 /* Handler to get job status */
2138 static int status_handler(void *ctx, int num_fields, char **row)
2139 {
2140    char *val = (char *)ctx;
2141
2142    if (row[0]) {
2143       *val = row[0][0];
2144    } else {
2145       *val = '?';               /* Unknown by default */
2146    }
2147
2148    return 0;
2149 }
2150
2151 /*
2152  * Wait until no job is running
2153  */
2154 int wait_cmd(UAContext *ua, const char *cmd)
2155 {
2156    JCR *jcr;
2157    int i;
2158    time_t stop_time = 0;
2159
2160    /*
2161     * no args
2162     * Wait until no job is running
2163     */
2164    if (ua->argc == 1) {
2165       bmicrosleep(0, 200000);            /* let job actually start */
2166       for (bool running=true; running; ) {
2167          running = false;
2168          foreach_jcr(jcr) {
2169             if (!jcr->is_internal_job()) {
2170                running = true;
2171                break;
2172             }
2173          }
2174          endeach_jcr(jcr);
2175
2176          if (running) {
2177             bmicrosleep(1, 0);
2178          }
2179       }
2180       return 1;
2181    }
2182
2183    i = find_arg_with_value(ua, NT_("timeout"));
2184    if (i > 0 && ua->argv[i]) {
2185       stop_time = time(NULL) + str_to_int64(ua->argv[i]);
2186    }
2187
2188    /* we have jobid, jobname or ujobid argument */
2189
2190    uint32_t jobid = 0 ;
2191
2192    if (!open_client_db(ua)) {
2193       ua->error_msg(_("ERR: Can't open db\n")) ;
2194       return 1;
2195    }
2196
2197    for (int i=1; i<ua->argc; i++) {
2198       if (strcasecmp(ua->argk[i], "jobid") == 0) {
2199          if (!ua->argv[i]) {
2200             break;
2201          }
2202          jobid = str_to_int64(ua->argv[i]);
2203          break;
2204       } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
2205                  strcasecmp(ua->argk[i], "job") == 0) {
2206          if (!ua->argv[i]) {
2207             break;
2208          }
2209          jcr=get_jcr_by_partial_name(ua->argv[i]) ;
2210          if (jcr) {
2211             jobid = jcr->JobId ;
2212             free_jcr(jcr);
2213          }
2214          break;
2215       } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
2216          if (!ua->argv[i]) {
2217             break;
2218          }
2219          jcr=get_jcr_by_full_name(ua->argv[i]) ;
2220          if (jcr) {
2221             jobid = jcr->JobId ;
2222             free_jcr(jcr);
2223          }
2224          break;
2225       /* Wait for a mount request */
2226       } else if (strcasecmp(ua->argk[i], "mount") == 0) {
2227          for (bool waiting=false; !waiting; ) {
2228             foreach_jcr(jcr) {
2229                if (!jcr->is_internal_job() &&
2230                    (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount ||
2231                     jcr->SDJobStatus == JS_WaitMedia || jcr->SDJobStatus == JS_WaitMount))
2232                {
2233                   waiting = true;
2234                   break;
2235                }
2236             }
2237             endeach_jcr(jcr);
2238             if (waiting) {
2239                break;
2240             }
2241             if (stop_time && (time(NULL) >= stop_time)) {
2242                ua->warning_msg(_("Wait on mount timed out\n"));
2243                return 1;
2244             }
2245             bmicrosleep(1, 0);
2246          }
2247          return 1;
2248       }
2249    }
2250
2251    if (jobid == 0) {
2252       ua->error_msg(_("ERR: Job was not found\n"));
2253       return 1 ;
2254    }
2255
2256    /*
2257     * We wait the end of a specific job
2258     */
2259
2260    bmicrosleep(0, 200000);            /* let job actually start */
2261    for (bool running=true; running; ) {
2262       running = false;
2263
2264       jcr=get_jcr_by_id(jobid) ;
2265
2266       if (jcr) {
2267          running = true ;
2268          free_jcr(jcr);
2269       }
2270
2271       if (running) {
2272          bmicrosleep(1, 0);
2273       }
2274    }
2275
2276    /*
2277     * We have to get JobStatus
2278     */
2279
2280    int status ;
2281    char jobstatus = '?';        /* Unknown by default */
2282    char buf[256] ;
2283
2284    bsnprintf(buf, sizeof(buf),
2285              "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
2286
2287
2288    db_sql_query(ua->db, buf, status_handler, (void *)&jobstatus);
2289
2290    switch (jobstatus) {
2291    case JS_Error:
2292       status = 1 ;         /* Warning */
2293       break;
2294
2295    case JS_Incomplete:
2296    case JS_FatalError:
2297    case JS_ErrorTerminated:
2298    case JS_Canceled:
2299       status = 2 ;         /* Critical */
2300       break;
2301
2302    case JS_Warnings:
2303    case JS_Terminated:
2304       status = 0 ;         /* Ok */
2305       break;
2306
2307    default:
2308       status = 3 ;         /* Unknown */
2309       break;
2310    }
2311
2312    ua->send_msg("JobId=%i\n", jobid) ;
2313    ua->send_msg("JobStatus=%s (%c)\n",
2314                 job_status_to_str(jobstatus, 0),
2315                 jobstatus) ;
2316
2317    if (ua->gui || ua->api) {
2318       ua->send_msg("ExitStatus=%i\n", status) ;
2319    }
2320
2321    return 1;
2322 }
2323
2324
2325 static int help_cmd(UAContext *ua, const char *cmd)
2326 {
2327    int i;
2328    ua->send_msg(_("  Command       Description\n  =======       ===========\n"));
2329    for (i=0; i<comsize; i++) {
2330       if (ua->argc == 2) {
2331          if (!strcasecmp(ua->argk[1], commands[i].key)) {
2332             ua->send_msg(_("  %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
2333                          commands[i].help, commands[i].usage);
2334             break;
2335          }
2336       } else {
2337          ua->send_msg(_("  %-13s %s\n"), commands[i].key, commands[i].help);
2338       }
2339    }
2340    if (i == comsize && ua->argc == 2) {
2341       ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
2342    }
2343    ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
2344    return 1;
2345 }
2346
2347 int qhelp_cmd(UAContext *ua, const char *cmd)
2348 {
2349    int i,j;
2350    /* Want to display only commands */
2351    j = find_arg(ua, NT_("all"));
2352    if (j >= 0) {
2353       for (i=0; i<comsize; i++) {
2354          ua->send_msg("%s\n", commands[i].key);
2355       }
2356       return 1;
2357    }
2358    /* Want to display a specific help section */
2359    j = find_arg_with_value(ua, NT_("item"));
2360    if (j >= 0 && ua->argk[j]) {
2361       for (i=0; i<comsize; i++) {
2362          if (bstrcmp(commands[i].key, ua->argv[j])) {
2363             ua->send_msg("%s\n", commands[i].usage);
2364             break;
2365          }
2366       }
2367       return 1;
2368    }
2369    /* Want to display everything */
2370    for (i=0; i<comsize; i++) {
2371       ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
2372    }
2373    return 1;
2374 }
2375
2376 #if 1
2377 static int version_cmd(UAContext *ua, const char *cmd)
2378 {
2379    ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
2380                 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
2381    return 1;
2382 }
2383 #else
2384 /*
2385  *  Test code -- turned on only for debug testing
2386  */
2387 static int version_cmd(UAContext *ua, const char *cmd)
2388 {
2389    dbid_list ids;
2390    POOL_MEM query(PM_MESSAGE);
2391    open_db(ua);
2392    Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
2393    db_get_query_dbids(ua->jcr, ua->db, query, ids);
2394    ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
2395    for (int i=0; i < ids.num_ids; i++) {
2396       ua->send_msg("id=%d\n", ids.DBId[i]);
2397    }
2398    close_db(ua);
2399    return 1;
2400 }
2401 #endif
2402
2403 /*
2404  * This call uses open_client_db() and force a
2405  * new dedicated connection to the catalog
2406  */
2407 bool open_new_client_db(UAContext *ua)
2408 {
2409    bool ret;
2410
2411    /* Force a new dedicated connection */
2412    ua->force_mult_db_connections = true;
2413    ret = open_client_db(ua);
2414    ua->force_mult_db_connections = false;
2415
2416    return ret;
2417 }
2418
2419 /*
2420  * This call explicitly checks for a catalog=xxx and
2421  *  if given, opens that catalog.  It also checks for
2422  *  client=xxx and if found, opens the catalog
2423  *  corresponding to that client. If we still don't
2424  *  have a catalog, look for a Job keyword and get the
2425  *  catalog from its client record.
2426  */
2427 bool open_client_db(UAContext *ua)
2428 {
2429    int i;
2430    CAT *catalog;
2431    CLIENT *client;
2432    JOB *job;
2433
2434    /* Try for catalog keyword */
2435    i = find_arg_with_value(ua, NT_("catalog"));
2436    if (i >= 0) {
2437       if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
2438          ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
2439          return false;
2440       }
2441       catalog = GetCatalogResWithName(ua->argv[i]);
2442       if (catalog) {
2443          if (ua->catalog && ua->catalog != catalog) {
2444             close_db(ua);
2445          }
2446          ua->catalog = catalog;
2447          return open_db(ua);
2448       }
2449    }
2450
2451    /* Try for client keyword */
2452    i = find_arg_with_value(ua, NT_("client"));
2453    if (i >= 0) {
2454       if (!acl_access_client_ok(ua, ua->argv[i], JT_BACKUP_RESTORE)) {
2455          ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2456          return false;
2457       }
2458       client = GetClientResWithName(ua->argv[i]);
2459       if (client) {
2460          catalog = client->catalog;
2461          if (ua->catalog && ua->catalog != catalog) {
2462             close_db(ua);
2463          }
2464          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2465             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2466             return false;
2467          }
2468          ua->catalog = catalog;
2469          return open_db(ua);
2470       }
2471    }
2472
2473    /* Try for Job keyword */
2474    i = find_arg_with_value(ua, NT_("job"));
2475    if (i >= 0) {
2476       if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2477          ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2478          return false;
2479       }
2480       job = GetJobResWithName(ua->argv[i]);
2481       if (job) {
2482          catalog = job->client->catalog;
2483          if (ua->catalog && ua->catalog != catalog) {
2484             close_db(ua);
2485          }
2486          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2487             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2488             return false;
2489          }
2490          ua->catalog = catalog;
2491          return open_db(ua);
2492       }
2493    }
2494
2495    return open_db(ua);
2496 }
2497
2498
2499 /*
2500  * Open the catalog database.
2501  */
2502 bool open_db(UAContext *ua)
2503 {
2504    bool mult_db_conn;
2505
2506    /* With a restricted console, we can't share a SQL connection */
2507    if (ua->cons) {
2508       ua->force_mult_db_connections = true;
2509    }
2510
2511    /* The force_mult_db_connections is telling us if we modify the
2512     * private or the shared link
2513     */
2514    if (ua->force_mult_db_connections) {
2515       ua->db = ua->private_db;
2516
2517    } else {
2518       ua->db = ua->shared_db;
2519    }
2520
2521    if (ua->db) {
2522       return true;
2523    }
2524
2525    if (!ua->catalog) {
2526       ua->catalog = get_catalog_resource(ua);
2527       if (!ua->catalog) {
2528          ua->error_msg( _("Could not find a Catalog resource\n"));
2529          return false;
2530       }
2531    }
2532
2533    /* Some modules like bvfs need their own catalog connection */
2534    mult_db_conn = ua->catalog->mult_db_connections;
2535    if (ua->force_mult_db_connections) {
2536       mult_db_conn = true;
2537    }
2538
2539    ua->jcr->catalog = ua->catalog;
2540
2541    Dmsg0(100, "UA Open database\n");
2542    ua->db = db_init_database(ua->jcr, ua->catalog->db_driver,
2543                              ua->catalog->db_name,
2544                              ua->catalog->db_user,
2545                              ua->catalog->db_password, ua->catalog->db_address,
2546                              ua->catalog->db_port, ua->catalog->db_socket,
2547                              ua->catalog->db_ssl_mode, ua->catalog->db_ssl_key,
2548                              ua->catalog->db_ssl_cert, ua->catalog->db_ssl_ca,
2549                              ua->catalog->db_ssl_capath, ua->catalog->db_ssl_cipher,
2550                              mult_db_conn, ua->catalog->disable_batch_insert); 
2551    if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2552       ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2553                  ua->catalog->db_name);
2554       if (ua->db) {
2555          ua->error_msg("%s", db_strerror(ua->db));
2556       }
2557       close_db(ua);
2558       return false;
2559    }
2560    ua->jcr->db = ua->db;
2561
2562    /* Depending on the type of connection, we set the right variable */
2563    if (ua->force_mult_db_connections) {
2564       ua->private_db = ua->db;
2565
2566    } else {
2567       ua->shared_db = ua->db;
2568    }
2569    /* With a restricted console, the DB backend should know restrictions about
2570     * Pool, Job, etc...
2571     */
2572    if (ua->cons) {
2573       ua->db->set_acl(ua->jcr, DB_ACL_JOB, ua->cons->ACL_lists[Job_ACL]);
2574       ua->db->set_acl(ua->jcr, DB_ACL_CLIENT, ua->cons->ACL_lists[Client_ACL]);
2575       ua->db->set_acl(ua->jcr, DB_ACL_POOL, ua->cons->ACL_lists[Pool_ACL]);
2576       ua->db->set_acl(ua->jcr, DB_ACL_FILESET, ua->cons->ACL_lists[FileSet_ACL]);
2577
2578       /* For RestoreClient and BackupClient, we take also in account the Client list */
2579       ua->db->set_acl(ua->jcr, DB_ACL_RCLIENT,
2580                       ua->cons->ACL_lists[Client_ACL],
2581                       ua->cons->ACL_lists[RestoreClient_ACL]);
2582
2583       ua->db->set_acl(ua->jcr, DB_ACL_BCLIENT,
2584                       ua->cons->ACL_lists[Client_ACL],
2585                       ua->cons->ACL_lists[BackupClient_ACL]);
2586    }
2587    if (!ua->api) {
2588       ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2589    }
2590    Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2591    return true;
2592 }
2593
2594 void close_db(UAContext *ua)
2595 {
2596    if (ua->jcr) {
2597       ua->jcr->db = NULL;
2598    }
2599
2600    if (ua->shared_db) {
2601       db_close_database(ua->jcr, ua->shared_db);
2602       ua->shared_db = NULL;
2603    }
2604
2605    if (ua->private_db) {
2606       db_close_database(ua->jcr, ua->private_db);
2607       ua->private_db = NULL;
2608    }
2609
2610    ua->db = NULL;
2611 }